diff --git a/fastlib/u/nvasil/tpie/ami.h b/fastlib/u/nvasil/tpie/ami.h new file mode 100644 index 0000000000..32450fc35d --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami.h @@ -0,0 +1,42 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: ami.h +// Author: Darren Erik Vengroff +// 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 + +// Get a stream implementation. +#include + +// Get templates for ami_scan(). +#include + +// Get templates for ami_merge(). +#include + +// Get templates for ami_sort(). +#include + +// Get templates for general permutation. +#include + +// Get templates for bit permuting. +#include + +// Get a collection implementation. +#include + +// Get a block implementation. +#include + +// Get templates for AMI_btree. +#include + +#endif // _AMI_H diff --git a/fastlib/u/nvasil/tpie/ami_bit_permute.cc b/fastlib/u/nvasil/tpie/ami_bit_permute.cc new file mode 100644 index 0000000000..0bf3d67e9e --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_bit_permute.cc @@ -0,0 +1,35 @@ +// Copyright (c) 1995 Darren Vengroff +// +// File: ami_bit_permute.cpp +// Author: Darren Vengroff +// Created: 1/9/95 +// + +#include + +#include +VERSION(ami_bit_permute_cpp,"$Id: ami_bit_permute.cpp,v 1.4 2003/04/20 06:44:01 tavi Exp $"); + +#include + +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; +} + + diff --git a/fastlib/u/nvasil/tpie/ami_bit_permute.h b/fastlib/u/nvasil/tpie/ami_bit_permute.h new file mode 100644 index 0000000000..ab7647c48e --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_bit_permute.h @@ -0,0 +1,126 @@ +// Copyright (c) 1995 Darren Vengroff +// +// File: ami_bit_permute.h +// Author: Darren Vengroff +// 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 + +// Get bit_matrix. +#include + +// Get AMI_gen_perm_object. +#include +// Get the AMI_general_permute(). +#include + +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 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 +AMI_err AMI_BMMC_permute(AMI_STREAM *instream, AMI_STREAM *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 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 diff --git a/fastlib/u/nvasil/tpie/ami_block.h b/fastlib/u/nvasil/tpie/ami_block.h new file mode 100644 index 0000000000..a04d68aeaf --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_block.h @@ -0,0 +1,98 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: ami_block.h +// Authors: Octavian Procopiuc +// +// 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 + +// The AMI_block_base class. +#include + +// The b_vector class. +#include + +template +class AMI_block: public AMI_block_base { + protected: + using AMI_block_base::bid_; + using AMI_block_base::dirty_; + using AMI_block_base::pdata_; + using AMI_block_base::per_; + using AMI_block_base::pcoll_; + + public: + using AMI_block_base::bid; + + // typedef typename BTECOLL::block_id_t id_t; + + // The array of links. + b_vector lk; + + // The array of elements. + b_vector el; + + typedef typename b_vector::iterator lk_iterator; + typedef typename b_vector::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* 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 +size_t AMI_block::el_capacity(size_t block_size, size_t links) { + return (size_t) ((block_size - sizeof(I) - links * sizeof(AMI_bid)) / sizeof(E)); +} + +template +AMI_block::AMI_block(AMI_collection_single* pacoll, + size_t links, AMI_bid _bid): + AMI_block_base(pacoll, _bid), + lk((AMI_bid*)pdata_, links), + el((E*) ((char*) pdata_ + links * sizeof(AMI_bid)), + el_capacity(pcoll_->block_size(), links)) +{ +} + +template +I* AMI_block::info() { + return (I*) (((char*) pdata_ + (lk.capacity()*sizeof(AMI_bid) + + el.capacity()*sizeof(E)))); +} + +template +const I* AMI_block::info() const { + return (I*) (((char*) pdata_ + (lk.capacity()*sizeof(AMI_bid) + + el.capacity()*sizeof(E)))); +} + +#endif // _AMI_BLOCK_H diff --git a/fastlib/u/nvasil/tpie/ami_block_base.h b/fastlib/u/nvasil/tpie/ami_block_base.h new file mode 100644 index 0000000000..b251938af5 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_block_base.h @@ -0,0 +1,136 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: ami_block_base.h +// Author: Octavian Procopiuc +// +// $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 + +// The AMI error codes. +#include +// The AMI_COLLECTION class. +#include + +// 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 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* 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& operator=(const AMI_block_base& 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 diff --git a/fastlib/u/nvasil/tpie/ami_btree.h b/fastlib/u/nvasil/tpie/ami_btree.h new file mode 100644 index 0000000000..1a5f480986 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_btree.h @@ -0,0 +1,2499 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: ami_btree.h +// Author: Octavian Procopiuc +// +// $Id: ami_btree.h,v 1.35 2005/01/27 21:13:35 tavi Exp $ +// +// AMI_btree declaration and implementation. +// + +/** @file ami_btree.h + A templated implementation of a B+-tree. +*/ + +#ifndef _AMI_BTREE_H +#define _AMI_BTREE_H + +// STL files. +#include +#include +#include +#include +#include +#include +#include + +// Get a stream implementation. +#include +// Get templates for AMI_sort. +#include +// Get a collection implementation. +#include +// Get a block implementation. +#include +// The cache manager. +#include +// The tpie_stats_tree class for tree statistics. +#include +// The tpie_tempnam() function +#include + +/// Determines how elements are stored in a leaf. If set to 0, elements are +/// stored in the order in which they are inserted, which may results in +/// slower queries. If set to 1, elements are stored in a sorted list, which +/// may result in slower insertions when allowing duplicate keys (see +/// below). +#ifndef AMI_BTREE_LEAF_ELEMENTS_SORTED +# define AMI_BTREE_LEAF_ELEMENTS_SORTED 1 +#endif + +/// Determines whether to allow duplicate keys when inserting and bulk +/// loading. Support for duplicate keys is incomplete, so you might +/// experience errors when setting this to 0. +#ifndef AMI_BTREE_UNIQUE_KEYS +# define AMI_BTREE_UNIQUE_KEYS 1 +#endif + +/// Determines whether "previous" pointers are maintained for leaves. +/// Don't set to 0! There is good reason to maintain prev pointers: +/// computing predecessor queries. Unless maintaining previous pointers +/// proves costly, we keep them. +#ifndef AMI_BTREE_LEAF_PREV_POINTER +# define AMI_BTREE_LEAF_PREV_POINTER 1 +#endif + +enum AMI_btree_status { + AMI_BTREE_STATUS_VALID, + AMI_BTREE_STATUS_INVALID +}; + +/// Parameters for the AMI_btree. Passed to the AMI_btree constructor. +class AMI_btree_params { +public: + + /// Min number of Value's in a leaf. 0 means use default B-tree behavior. + TPIE_OS_SIZE_T leaf_size_min; + /// Min number of Key's in a node. 0 means use default B-tree behavior. + TPIE_OS_SIZE_T node_size_min; + /// 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; + + /// Set default parameter values. + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///
leaf_size_min0(default B-tree behavior; true value is set during B-tree construction)
node_size_min0(default B-tree behavior; true value is set during B-tree construction)
leaf_size_max0(default B-tree behavior; true value is set during B-tree construction)
node_size_max0(default B-tree behavior; true value is set during B-tree construction)
leaf_block_factor1
node_block_factor1
leaf_cache_size32
node_cache_size64
+ AMI_btree_params(): + leaf_size_min(0), node_size_min(0), + leaf_size_max(0), node_size_max(0), + leaf_block_factor(1), node_block_factor(1), + leaf_cache_size(32), node_cache_size(64) {} +}; + +/// A global object storing the default parameter values. +const AMI_btree_params btree_params_default = AMI_btree_params(); + +// Forward references. +template +class AMI_btree_leaf; +template +class AMI_btree_node; + +/** + An implementation of the B+-tree. + The AMI_btree class implements the + behavior of a dynamic B+-tree or (a,b)-tree storing fixed-size data + items. All data elements (of type Value) are stored in the leaves of + the tree, with internal nodes containing keys (of type Key) and links + to other nodes. The keys are ordered using the Compare function + object, which should define a strict weak ordering (as in the STL sorting + algorithms). Keys are extracted from the Value data elements using + the KeyOfValue function object. + + @param Key The key type. + @param Value The type of the data elements. + @param Compare A function object which defines a strict weak ordering for the keys. + @param KeyOfValue A function object for extracting a Key from a Value. + @param BTECOLL The underlying BTE collection type. It defaults to BTE_COLLECTION. + + Example of usage: test_ami_btree.cpp +*/ +template +class AMI_btree { +public: + + typedef AMI_btree_node node_t; + typedef AMI_btree_leaf leaf_t; + typedef AMI_collection_single collection_t; + typedef Key key_t; + typedef Value record_t; + typedef AMI_btree_params params_t; + + /** + Default filter for range queries. + This is a function object that returns true + (i.e., it lets every result of the query pass through). + */ + class dummy_filter_t { + public: + bool operator()(const Value& v) const { return true; } + }; + + + /** Construct an empty B-tree using temporary storage. + The tree is stored in a + directory given by the AMI_SINGLE_DEVICE environment variable + (or "/var/tmp/" if that variable is not set). The persistency flag is set to + PERSIST_DELETE. The params object contains the + user-definable parameters. + + @see AMI_btree_params. + */ + AMI_btree(const AMI_btree_params ¶ms = btree_params_default); + + + /** Construct a B-tree from the given leaves and nodes. + The files + created/used by a Btree instance are outlined in the following + table. + + + + + +
".l.blk"Contains the leaves block collection.
".l.stk"Contains the free blocks stack for the leaves block collection.
".n.blk"Contains the nodes block collection.
".n.stk"Contains the free block stack for the nodes block collection.
+ The persistency flag is + set to PERSIST_PERSISTENT. The params object contains the + user-definable parameters. + + @see AMI_btree_params, persist(). + */ + AMI_btree(const char *base_file_name, + AMI_collection_type type = AMI_WRITE_COLLECTION, + const AMI_btree_params ¶ms = btree_params_default); + + /** + @overload + */ + AMI_btree(const string& base_file_name, + AMI_collection_type type = AMI_WRITE_COLLECTION, + const AMI_btree_params ¶ms = btree_params_default); + + /** Sort in_stream and place the result in out_stream. + This is a convenience function, used as an initial step in bulk loading. + + If out_stream is NULL, a new temporary stream is created and out_stream + points to it. + + @see load(). + */ + AMI_err sort(AMI_STREAM* in_stream, AMI_STREAM* &out_stream); + + + /** + Bulk load the tree from a sorted stream. + Leaves are filled to leaf_fill times capacity, and nodes are filled to + node_fill times capacity. + */ + AMI_err load_sorted(AMI_STREAM* stream_s, + float leaf_fill = .75, float node_fill = .60); + + /** + Bulk load from given stream. + Calls sort() and then load_sorted(). + Leaves are filled to leaf_fill times capacity, and nodes are filled to + node_fill times capacity. + */ + AMI_err load(AMI_STREAM* s, + float leaf_fill = .75, float node_fill = .60); + + + /** + Write all elements stored in the tree to the given stream, in sorted order. + No changes are performed on the tree. + */ + AMI_err unload(AMI_STREAM* s); + + + /** + Bulk load from another B-tree. + This is a means of reoganizing a + B-tree after a lot of updates. A newly loaded structure may use less + space and may answer range queries faster. + Leaves are filled to leaf_fill times capacity, and nodes are filled to + node_fill times capacity. + */ + AMI_err load(AMI_btree* bt, + float leaf_fill = .75, float node_fill = .60); + + + /** + Traverse the tree in depth-first-search preorder. + Returns a pair containing the next node to be visited and its level (root is on level 0). + To initiate the process, the function should be called with -1 for level. + */ + pair dfs_preorder(int& level); + + + /** + Insert the given element into the tree. + Returns true if the insertion succeeded, false otherwise + (duplicate key) + */ + bool insert(const Value& v); + + + /** + Modify a given element. + If the given element is not found in the tree, it is inserted. + Equivalent to erase() followed by insert(), but using a single search operation. + */ + bool modify(const Value& v); + + + /** + Delete the element with the given key from the tree. + If an element was found and deleted, the function returns true. + Otherwise, it returns false. + */ + bool erase(const Key& k); + + + /** + Find an element based on the given key. + If found, return true and store the element in v. + Otherwise, return false. + */ + bool find(const Key& k, Value& v); + + + /** + Find the highest element stored in the tree whose key is lower than the given key. + If such an element exists, the function returns true and stores the result in + v. Otherwise, it returns false. + + @see succ() + */ + bool pred(const Key& k, Value& v); + + + /** + Find the lowest element stored in the tree whose key is higher than the given key. + If such an element exists, the function returns true and stores the result in + v. Otherwise, it returns false. + + @see pred() + */ + bool succ(const Key& k, Value& v); + + + /** + Find all elements within the given range. + If s is not NULL, the elements found are stored + in the stream and the number of elements found is returned. + Otherwise, the results are not stored, only the count is returned. + */ + // This method is inlined such as to comply with MSVC++ "requirements". + template + size_t range_query(const Key& k1, const Key& k2, + AMI_STREAM* s, const Filter& filter_through) +{ + + Key kmin = comp_(k1, k2) ? k1: k2; + Key kmax = comp_(k1, k2) ? k2: k1; + + // Find the leaf that might contain kmin. + AMI_bid bid = find_leaf(kmin); + AMI_btree_leaf *p = fetch_leaf(bid); + bool done = false; + size_t result = 0; + +#if AMI_BTREE_LEAF_ELEMENTS_SORTED + + size_t j; + j = p->find(kmin); + while (bid != 0 && !done) { + while (j < p->size() && !done) { + if (comp_(kov_(p->el[j]), kmax) || + (!comp_(kov_(p->el[j]), kmax) && !comp_(kmax, kov_(p->el[j])))) { + if (filter_through(p->el[j])) { + if (s != NULL) + s->write_item(p->el[j]); + result++; + } + } else + done = true; + j++; + } + bid = p->next(); + release_leaf(p); + if (bid != 0 && !done) + p = fetch_leaf(bid); + j = 0; + } + +#else + + size_t i; + // Check elements of p. + for (i = 0; i < p->size(); i++) { + if (comp_(kov_(p->el[i]), kmax) && comp_(kmin, kov_(p->el[i])) || + !comp_(kov_(p->el[i]), kmax) && !comp_(kmax, kov_(p->el[i])) || + !comp_(kov_(p->el[i]), kmin) && !comp_(kmin, kov_(p->el[i]))) { + if (filter_through(p->el[i])) { + if (s != NULL) + s->write_item(p->el[i]); + result++; + } + } + } + bid = p->next(); + release_leaf(p); + + if (bid != 0) { + p = fetch_leaf(bid); + AMI_bid pnbid = p->next(); + AMI_btree_leaf* pn; + + while (pnbid != 0 && !done) { + pn = fetch_leaf(pnbid); + if (comp_(kov_(pn->el[0]), kmax)) { + // Write all elements from p to stream s. + for (i = 0; i < p->size(); i++) { + if (filter_through(p->el[i])) { + if (s!= NULL) + s->write_item(p->el[i]); + result++; + } + } + } else + done = true; + + release_leaf(p); + p = pn; + pnbid = p->next(); + } + + // Check elements of p. + for (i = 0; i < p->size(); i++) { + if (comp_(kov_(p->el[i]), kmax) || + (!comp_(kov_(p->el[i]), kmax) && !comp_(kmax, kov_(p->el[i])))) { + if (filter_through(p->el[i])) { + if (s!= NULL) + s->write_item(p->el[i]); + result++; + } + } + } + release_leaf(p); + } +#endif + + empty_stack(); + return result; +} + + + /** + Find all elements within the given range. + If s is not NULL, the elements found are stored + in the stream and the number of elements found is returned. + Otherwise, the results are not stored, only the count is returned. + */ + TPIE_OS_OFFSET range_query(const Key& k1, const Key& k2, AMI_STREAM* s) + { return range_query(k1, k2, s, dummy_filter_t()); } + + + /** + Same as range_query(). + */ + template + size_t window_query(const Key& k1, const Key& k2, + AMI_STREAM* s, const Filter& f) + { return range_query(k1, k2, s, f); } + + + /** + Same as range_query(). + */ + TPIE_OS_OFFSET window_query(const Key& k1, const Key& k2, AMI_STREAM* s) + { return range_query(k1, k2, s, dummy_filter_t()); } + + + /** + Inquire the number of elements stored in the leaves of this tree. + */ + TPIE_OS_OFFSET size() const { return header_.size; } + + + /** + Inquire the number of leaf nodes of this tree. + */ + TPIE_OS_OFFSET leaf_count() const { return pcoll_leaves_->size(); } + + + /** + Inquire the number of internal (non-leaf) nodes of this tree. + */ + TPIE_OS_OFFSET node_count() const { return pcoll_nodes_->size(); } + + + TPIE_OS_OFFSET os_block_count() const { + return pcoll_leaves_->size() * params_.leaf_block_factor + + pcoll_nodes_->size() * params_.node_block_factor; + } + + + // Return the bid of the root. Make this protected or remove it. + AMI_bid root_bid() const { return header_.root_bid; } + + /** + Return the height of the tree, including the leaf level. + A value of 0 represents an empty tree. + A value of 1 represents a tree with only one leaf node (which is also the root node). + */ + TPIE_OS_SIZE_T height() const { return header_.height; } + + + /** + Set the persistency flag of the B-tree. + The persistency flag dictates the behavior of the destructor of + this AMI_btree instance. + If per is PERSIST_DELETE, all files + associated with the tree will be removed, and all the elements stored in + the tree will be lost after the destruction of this AMI_btree instance. + If per is PERSIST_PERSISTENT, all files associated with the tree + will be closed during the destruction, and all the + information needed to reopen this tree will be saved. + */ + void persist(persistence per); + + + /** + Return a const + reference to the AMI_btree_params object used by the B-tree. + This object contains the true values of all parameters (unlike the object + passed to the constructor, which may contain 0-valued parameters to + indicate default behavior). + + @see AMI_btree_params + */ + const AMI_btree_params& params() const { return params_; } + + + /** + Return the status of the collection. + The result is either + AMI_BTREE_STATUS_VALID or + AMI_BTREE_STATUS_INVALID. The only operation that can leave + the tree invalid is the constructor (if that happens, the log file + contains more information). + + @see is_valid() + */ + AMI_btree_status status() const { return status_; } + + + /** + Return true if the status + of the tree is AMI_BTREE_STATUS_VALID, false + otherwise. + + @see status() + */ + bool is_valid() const { return status_ == AMI_BTREE_STATUS_VALID; } + + + /** + Return an object containing the statistics of this B-tree. + The following statistics are collected: + + + + + + + + + +
BLOCK_GETNumber of block reads
BLOCK_PUTNumber of block writes
BLOCK_DELETENumber of block deletes
BLOCK_SYNCNumber of block sync operations
COLLECTION_OPENNumber of collection open operations
COLLECTION_CLOSENumber of collection close operations
COLLECTION_CREATENumber of collection create operations
COLLECTION_DELETENumber of collection delete operations
+ The statistics refer to this B-tree instance only. + + @see gstats() + */ + const tpie_stats_tree &stats(); + + /** + Inquire the base path name. + This is the name of the B-tree, determined during construction. + + @see AMI_btree() + */ + const string& name() const { return name_; } + + + /** + Close (and potentially destroy) this B-tree. + If the persistency flag is PERSIST_DELETE, all files + associated with the tree will be removed. + + @see persist() + */ + ~AMI_btree(); + +protected: + + + // Function object for the node cache write out. + class remove_node { + public: + void operator()(node_t* p) { delete p; } + }; + // Function object for the leaf cache write out. + class remove_leaf { + public: + void operator()(leaf_t* p) { delete p; } + }; + + typedef AMI_CACHE_MANAGER node_cache_t; + typedef AMI_CACHE_MANAGER leaf_cache_t; + + class header_t { + public: + AMI_bid root_bid; + TPIE_OS_SIZE_T height; + TPIE_OS_OFFSET size; + + header_t(): root_bid(0), height(0), size(0) {} + }; + + // Critical information: root bid, height, size (will be stored into + // the header of the nodes collection). + header_t header_; + + // The node cache. + node_cache_t* node_cache_; + // The leaf cache. + leaf_cache_t* leaf_cache_; + + // Run-time parameters. + AMI_btree_params params_; + + // The collection storing the leaves. + collection_t* pcoll_leaves_; + + // The collection storing the internal nodes (could be the same). + collection_t* pcoll_nodes_; + + // Comparison object. + Compare comp_; + + class comp_for_sort { + Compare comp_; + KeyOfValue kov_; + public: + int compare(const Value& v1, const Value& v2) { + return (comp_(kov_(v1), kov_(v2)) ? -1: + (comp_(kov_(v2), kov_(v1)) ? 1: 0)); + } + }; + + // The status. Set during construction. + AMI_btree_status status_; + + // Stack to store the path to a leaf. + stack >path_stack_; + + // Stack to store path during dfspreorder traversal. Each element is + // a pair: block id and link index. + stack >dfs_stack_; + + // Statistics. + tpie_stats_tree stats_; + + // Use this to obtain keys from Value elements. + KeyOfValue kov_; + + // Base path name. + string name_; + + // Insert helpers. + bool insert_split(const Value& v, + leaf_t* p, + AMI_bid& leaf_id, bool loading = false); + bool insert_empty(const Value& v); + bool insert_load(const Value& v, + leaf_t* &lcl); + + // Intialization routine shared by all constructors. + void shared_init(const char* base_file_name, AMI_collection_type type); + + // Empty the path stack. + void empty_stack() { while (!path_stack_.empty()) path_stack_.pop(); } + + // Find the leaf where an element with key k might be. Return the + // bid of that leaf. The stack contains the path to that leaf (but + // not the leaf itself). Each item in the stack is a pair of a bid + // of a node and the position (in this node) of the link to the son + // that is next on the path to the leaf. + AMI_bid find_leaf(const Key& k); + + // Return the leaf with the minimum key element. Nothing is pushed + // on the stack. + AMI_bid find_min_leaf(); + + // Return true if leaf p is underflow. + bool underflow_leaf(leaf_t *p) const; + + // Return true if node p is underflow. + bool underflow_node(node_t *p) const; + + // Return the underflow size of a leaf. Moved this function from the + // leaf class here for saving the space of the minimum fanout, a. + TPIE_OS_SIZE_T cutoff_leaf(leaf_t *p) const; + + // Return the underflow size of a node. Moved this function from the + // node class here for saving the space of the minimum fanout, a. + TPIE_OS_SIZE_T cutoff_node(node_t *p) const; + + // Return true if leaf p is full. + bool full_leaf(const leaf_t *p) const; + + // Return true if node p is full. + bool full_node(const node_t *p) const; + + // Try to balance p (when underflow) by borrowing one element from a sibling. + // f is the father of p and pos is the position of the link to p in f. + // Return false if unsuccessful. + bool balance_leaf(node_t *f, + leaf_t *p, size_t pos); + + // Same as above, but p is a node. + bool balance_node(node_t *f, + node_t *p, size_t pos); + + // (When balancing fails,) merge p with a sibling. f is the father + // of p and pos is the position of the link to p in f. + void merge_leaf(node_t *f, + leaf_t* &p, size_t pos); + + // Same as above, but p is a node. + void merge_node(node_t *f, + node_t* &p, size_t pos); + +public: + node_t* fetch_node(AMI_bid bid = 0); + leaf_t* fetch_leaf(AMI_bid bid = 0); + + void release_leaf(leaf_t* p); + void release_node(node_t* p); +}; + + + + +// Define shortcuts. They are undefined at the end of the file. +#define AMI_BTREE_NODE AMI_btree_node +#define AMI_BTREE_LEAF AMI_btree_leaf +#define AMI_BTREE AMI_btree + + + +// The Info element of a leaf. +struct _AMI_btree_leaf_info { + TPIE_OS_SIZE_T size; + AMI_bid prev; + AMI_bid next; +}; + +// The AMI_btree_leaf class. +// Stores size() elements of type Value. +template +class AMI_btree_leaf: public AMI_block { + + Compare comp_; + + // This is a hack. It allows comparison between + // Values and Keys for STL's lower_bound(). + struct Compare_value_key { + bool operator()(const Value& v, const Key& k) const { + return Compare()(KeyOfValue()(v), k); + } + }; + struct Compare_value_value { + bool operator()(const Value& v1, const Value& v2) const { + return Compare()(KeyOfValue()(v1), KeyOfValue()(v2)); + } + }; + Compare_value_key comp_value_key_; + Compare_value_value comp_value_value_; + +public: + using AMI_block::info; + using AMI_block::el; + using AMI_block::dirty; + + // Compute the capacity of the el vector STATICALLY (but you have to + // give it the correct logical block size!). + static TPIE_OS_SIZE_T el_capacity(size_t block_size); + + // Find and return the position of key k + // (ie, the lowest position where it would be inserted). + TPIE_OS_SIZE_T find(const Key& k); + + // Predecessor of k. + TPIE_OS_SIZE_T pred(const Key& k); + + // Successor of k. + TPIE_OS_SIZE_T succ(const Key& k); + + // Constructor. + AMI_btree_leaf(AMI_collection_single* pcoll, AMI_bid bid = 0); + + // Number of elements stored in this leaf. + TPIE_OS_SIZE_T & size() { return info()->size; } + const TPIE_OS_SIZE_T & size() const { return info()->size; } + + // Maximum number of elements that can be stored in this leaf. + TPIE_OS_SIZE_T capacity() const { return el.capacity(); } + + AMI_bid& prev() { return info()->prev; } + const AMI_bid& prev() const { return info()->prev; } + + AMI_bid& next() { return info()->next; } + const AMI_bid& next() const { return info()->next; } + + bool full() const { return size() == capacity(); } + + bool empty() const { return size() == 0; } + + // Split into two leaves containing the same number of elements. + // Return the median key (ie, the key of the last elem. stored + // in this leaf, after split). + Key split(AMI_BTREE_LEAF &right); + + // Merge this leaf with another leaf. + void merge(const AMI_BTREE_LEAF &right); + + // Insert a data element. The leaf should NOT be full. + // Return false if the key is already in the tree. + bool insert(const Value& v); + + // Insert element into position pos. + void insert_pos(const Value& v, TPIE_OS_SIZE_T pos); + + // Delete an element given by its key. The leaf should NOT be empty. + // Return false if the key is not found in the tree. + bool erase(const Key& k); + + // Erase element from position pos. + void erase_pos(size_t pos); + + // Sort elements. + void sort(); + + // Destructor. + ~AMI_btree_leaf(); +}; + +// The AMI_btree_node class. +// An internal node of the AMI_btree. +// It stores size() keys and size()+1 links representing +// the following pattern: Link0 Key0 Link1 Key1 ... LinkS KeyS Link(S+1) +template +class AMI_btree_node: public AMI_block { + + Compare comp_; + +public: + using AMI_block::info; + using AMI_block::el; + using AMI_block::lk; + using AMI_block::dirty; + + // Compute the capacity of the lk vector STATICALLY (but you have to + // give it the correct logical block size!). + static size_t lk_capacity(size_t block_size); + // Compute the capacity of the el vector STATICALLY. + static TPIE_OS_SIZE_T el_capacity(size_t block_size); + + // Find and return the position of key k + // (ie, the lowest position in the array of keys where it would be inserted). + TPIE_OS_SIZE_T find(const Key& k); + + // Constructor. Calls the block constructor with the + // appropriate number of links. + AMI_btree_node(AMI_collection_single* pcoll, AMI_bid bid = 0); + + // Number of keys stored in this node. + TPIE_OS_SIZE_T& size() { return (TPIE_OS_SIZE_T&) (*info()); } + const TPIE_OS_SIZE_T& size() const { return (TPIE_OS_SIZE_T&) (*info()); } + + // Maximum number of keys that can be stored in this node. + TPIE_OS_SIZE_T capacity() const { return el.capacity(); } + + bool full() const { return size() == capacity(); } + + bool empty() const { return size() == 0; } + + // Split into two leaves containing the same number of elements. + // Return the median key, to be stored in the father node. + Key split(AMI_BTREE_NODE &right); + + // Merge this node with another node. + void merge(const AMI_BTREE_NODE &right, const Key& k); + + // Insert a key and link into a non-full node in a given position. + // No validity checks. + void insert_pos(const Key& k, AMI_bid l, TPIE_OS_SIZE_T k_pos, TPIE_OS_SIZE_T l_pos); + + // Insert a key and link into a non-full node + // (uses the key k to find the right position). + void insert(const Key& k, AMI_bid l); + + // Delete an element given by its key. + void erase_pos(size_t k_pos, size_t l_pos); + + ~AMI_btree_node(); +}; + + +////////////////////////////////////////////////////////// +///////////////// ***Implementation*** /////////////////// +////////////////////////////////////////////////////////// + + +//////////////////////////////////// +//////// **AMI_btree_leaf** //////// +//////////////////////////////////// + +template +TPIE_OS_SIZE_T AMI_BTREE_LEAF::el_capacity(TPIE_OS_SIZE_T block_size) { + return AMI_block::el_capacity(block_size, 0); +} + +//// *AMI_btree_leaf::AMI_btree_leaf* //// +template +AMI_BTREE_LEAF::AMI_btree_leaf(AMI_collection_single* pcoll, AMI_bid lbid) + : AMI_block(pcoll, 0, lbid) { + if (lbid == 0) { + size() = 0; + next() = 0; +#if AMI_BTREE_LEAF_PREV_POINTER + prev() = 0; +#endif + } +} + +//// *AMI_btree_leaf::split* //// +template +Key AMI_BTREE_LEAF::split(AMI_BTREE_LEAF &right) { + +#if (!AMI_BTREE_LEAF_ELEMENTS_SORTED) + sort(); +#endif + + // save the original size of this leaf. + TPIE_OS_SIZE_T original_size = size(); + + // The new leaf will have half of this leaf's elements. + // If the original size is odd, the new leaf will have fewer elements. + right.size() = original_size / 2; + + // Update this leaf's size. + size() = original_size - right.size(); + + // Copy the elements of the new leaf from the end + // of this leaf's array of elements. + right.el.copy(0, right.size(), el, size()); + + dirty() = 1; + right.dirty() = 1; + + // Return the key of the last element from this leaf. + return KeyOfValue()(el[size() - 1]); +} + +//// *AMI_btree_leaf::merge* //// +template +void AMI_BTREE_LEAF::merge(const AMI_BTREE_LEAF &right) { + + // Make sure there's enough place. + assert(size() + right.size() <= capacity()); + +#if AMI_BTREE_LEAF_ELEMENTS_SORTED + assert(comp_(KeyOfValue()(el[size() - 1]), KeyOfValue()(right.el[0]))); +#endif + + // save the original size of this leaf. + TPIE_OS_SIZE_T original_size = size(); + + // Update this leaf's size. + size() = original_size + right.size(); + + // Copy the elements of the right leaf to the end + // of this leaf's array of elements. + el.copy(original_size, right.size(), right.el, 0); + + next() = right.next(); + dirty() = 1; +} + +//// *AMI_btree_leaf::insert_pos* //// +template +inline void AMI_BTREE_LEAF::insert_pos(const Value& v, size_t pos) { + + // Insert mechanics. + if (pos == size()) + el[pos] = v; + else + el.insert(v, pos); + + // Increase size by one and update the dirty bit. + size()++; + dirty() = 1; +} + +//// *AMI_btree_leaf::insert* //// +template +inline bool AMI_BTREE_LEAF::insert(const Value& v) { + +#if (AMI_BTREE_LEAF_ELEMENTS_SORTED || AMI_BTREE_UNIQUE_KEYS) + // Find the position where v should be. + size_t pos; + if (size() == 0) + pos = 0; + else if (comp_(KeyOfValue()(el[size()-1]), KeyOfValue()(v))) + pos = size(); + else + pos = find(KeyOfValue()(v)); +#endif + +#if AMI_BTREE_UNIQUE_KEYS + // Check for duplicate key. + if (pos < size()) + if (!comp_(KeyOfValue()(v), KeyOfValue()(el[pos])) && + !comp_(KeyOfValue()(el[pos]), KeyOfValue()(v))) { + TP_LOG_WARNING_ID("Attempting to insert duplicate key. Ignoring insert."); + return false; + } +#endif + +#if AMI_BTREE_LEAF_ELEMENTS_SORTED + insert_pos(v, pos); +#else + insert_pos(v, size()); +#endif + + return true; +} + +//// *AMI_btree_leaf::find* //// +template +size_t AMI_BTREE_LEAF::find(const Key& k) { +#if AMI_BTREE_LEAF_ELEMENTS_SORTED + // Sanity check. + assert(size() < 2 || comp_(KeyOfValue()(el[0]), KeyOfValue()(el[size()-1]))); + return lower_bound(&el[0], &el[size()-1] + 1, k, comp_value_key_) - &el[0]; +#else + size_t i; + for (i = 0u; i < size(); i++) + if (!comp_(KeyOfValue()(el[i]), k) && !comp_(k, KeyOfValue()(el[i]))) + return i; + return size(); +#endif +} + +//// *AMI_btree_leaf::pred* //// +template +size_t AMI_BTREE_LEAF::pred(const Key& k) { + +size_t pred_idx; +#if AMI_BTREE_LEAF_ELEMENTS_SORTED + // Sanity check. + assert(size() < 2 || comp_(KeyOfValue()(el[0]), KeyOfValue()(el[size()-1]))); + pred_idx = lower_bound(&el[0], &el[size()-1] + 1, k,comp_value_key_) - &el[0]; + // lower_bound pos is off by one for pred + // Final pred_idx cannot be matching key + if (pred_idx != 0) + pred_idx--; + return pred_idx; +#else + size_t i=0; + size_t j; + // Find candidate + while (i < size() && !comp_(KeyOfValue()(el[i]), k) ) + i++; + pred_idx = i; + // Check for closer candidates + for (j = i+1; j < size(); j++) + if (comp_(KeyOfValue()(el[j]), k) && comp_(KeyOfValue()(el[i]), KeyOfValue()(el[j]))) + pred_idx = j; + + return ((i != size()) ? pred_idx: 0); +#endif +} + +//// *AMI_btree_leaf::succ* //// +template +size_t AMI_BTREE_LEAF::succ(const Key& k) { + +size_t succ_idx; +#if AMI_BTREE_LEAF_ELEMENTS_SORTED + // Sanity check. + assert(size() < 2 || comp_(KeyOfValue()(el[0]), KeyOfValue()(el[size()-1]))); + succ_idx = lower_bound(&el[0], &el[size()-1] + 1, k, comp_value_key_) - &el[0]; + // Bump up one spot if keys match + if (succ_idx != size() && + !comp_(k,KeyOfValue()(el[succ_idx])) && !comp_(KeyOfValue()(el[succ_idx]),k) ) + succ_idx++; + return succ_idx; +#else + size_t i=0; + size_t j; + + // Find candidate + while (i < size() && !comp_(k, KeyOfValue()(el[i])) ) + i++; + succ_idx = i; + // Check for closer candidates + for (j = i+1; j < size(); j++) + if (comp_(k, KeyOfValue()(el[j])) && comp_(KeyOfValue()(el[j]), KeyOfValue()(el[i]))) + succ_idx = j; + + return ((i != size()) ? succ_idx: 0); +#endif +} + +//// *AMI_btree_leaf::erase* //// +template +bool AMI_BTREE_LEAF::erase(const Key& k) { + + // Sanity check. + assert(!empty()); + + // Find the position where k should be. + size_t pos = find(k); + + // Make sure we found an exact match. + if (pos == size()) + return false; + // TODO: make sure this is right. + if (comp_(KeyOfValue()(el[pos]), k) || comp_(k, KeyOfValue()(el[pos]))) + return false; + + erase_pos(pos); + + return true; +} + +//// *AMI_btree_leaf::erase_pos* //// +template +void AMI_BTREE_LEAF::erase_pos(TPIE_OS_SIZE_T pos) { + + // Erase mechanics. + el.erase(pos); + + // Decrease size by one and update dirty bit. + size()--; + dirty() = 1; +} + +//// *AMI_btree_leaf::sort* //// +template +void AMI_BTREE_LEAF::sort() { + sort(&el[0], &el[size()-1] + 1, comp_value_value_); +} + +//// *AMI_btree_leaf::~AMI_btree_leaf* //// +template +AMI_BTREE_LEAF::~AMI_btree_leaf() { + // TODO: is there anything to do here? +} + + +//////////////////////////////// +//////// **AMI_btree_node** //////// +//////////////////////////////// + +template +size_t AMI_BTREE_NODE::lk_capacity(size_t block_size) { + return (size_t) ((block_size - sizeof(size_t) - sizeof(AMI_bid)) / + (sizeof(Key) + sizeof(AMI_bid)) + 1); +} + +template +TPIE_OS_SIZE_T AMI_BTREE_NODE::el_capacity(TPIE_OS_SIZE_T block_size) { + // Sanity check. Two different methods of computing the el capacity. + // [tavi 01/26/02]: Changed == into >= since I could fit one more + // element, but not one more link. + assert((AMI_block::el_capacity(block_size, lk_capacity(block_size))) >= (TPIE_OS_SIZE_T) (lk_capacity(block_size) - 1)); + return (TPIE_OS_SIZE_T) (lk_capacity(block_size) - 1); +} + +//// *AMI_btree_node::AMI_btree_node* //// +template +AMI_BTREE_NODE::AMI_btree_node(AMI_collection_single* pcoll, AMI_bid nbid): + AMI_block(pcoll, lk_capacity(pcoll->block_size()), nbid) { + if (nbid == 0) + size() = 0; +} + + +//// *AMI_btree_node::split* //// +template +Key AMI_BTREE_NODE::split(AMI_BTREE_NODE &right) { + + // TODO: Is this needed? I want to be left with at least one key in each + // node. + assert(size() >= 3); + + // save the original size of this node. + size_t original_size = size(); + + // The new node will have half of this node's keys and half of its links. + right.size() = original_size / 2; + + // Update this node's size (subtract one to account for the key + // that is going up the tree). + size() = original_size - right.size() - 1; + + // Copy the keys of the new node from the end of this node's array of keys. + //memcpy(right.elem(0), elem(size()+1), right.size() * sizeof(Key)); + right.el.copy(0, right.size(), el, size() + 1); + + // Copy the links of the new node from the end of this node's array of links. + //memcpy(right.link(0), link(size()+1), (right.size()+1) * sizeof(AMI_bid)); + right.lk.copy(0, right.size() + 1, lk, size() + 1); + + dirty() = 1; + right.dirty() = 1; + + // Return a copy of the key past the last key (no longer stored here). + return el[size()]; +} + +//// *AMI_btree_node::insert_pos* //// +template +void AMI_BTREE_NODE::insert_pos(const Key& k, AMI_bid l, size_t k_pos, size_t l_pos) { + + assert(!full()); + + // Insert mechanics. + if (k_pos == size()) + el[k_pos] = k; + else + el.insert(k, k_pos); + + if (l_pos == size() + 1) + lk[l_pos] = l; + else + lk.insert(l, l_pos); + + // Update size and dirty bit. + size()++; + dirty() = 1; +} + + +//// *AMI_btree_node::insert* //// +template +void AMI_BTREE_NODE::insert(const Key& k, AMI_bid l) { + + // Find the position using STL's binary search. + size_t pos = lower_bound(&el[0], &el[size()-1] + 1, k, comp_) - &el[0]; + + // Insert. + insert_pos(k, l, pos, pos + 1); +} + +//// *AMI_btree_node::erase_pos* //// +template +void AMI_BTREE_NODE::erase_pos(TPIE_OS_SIZE_T k_pos, TPIE_OS_SIZE_T l_pos) { + + assert(!empty()); + + // Erase mechanics. + el.erase(k_pos); + lk.erase(l_pos); + + // Update the size and dirty bit. + size()--; + dirty() = 1; +} + + +//// *AMI_btree_node::merge* //// +template +void AMI_BTREE_NODE::merge(const AMI_BTREE_NODE &right, const Key& k) { + + // Make sure there's enough place. + assert(size() + right.size() + 1 <= capacity()); + + // save the original size of this leaf. + size_t original_size = size(); + + // Update this leaf's size. We add one to account for the key + // that's added in-between. + size() = original_size + right.size() + 1; + + // Copy the elements of the right leaf to the end of this leaf's + // array of elements. + el[original_size] = k; + el.copy(original_size + 1, right.size(), right.el, 0); + + // Copy the links also. + lk.copy(original_size + 1, right.size() + 1, right.lk, 0); + + dirty() = 1; +} + +//// *AMI_btree_node::find* //// +template +size_t AMI_BTREE_NODE::find(const Key& k) { + return (size() == 0) ? 0: (lower_bound(&el[0], &el[size()-1] + 1, k, comp_) - &el[0]); +} + +//// *AMI_btree_node::~AMI_btree_node* //// +template +AMI_BTREE_NODE::~AMI_btree_node() { + // TODO: is there anything to do here? +} + +/////////////////////////////// +//////// **AMI_btree** //////// +/////////////////////////////// + + +//// *AMI_btree::AMI_btree* //// +template +AMI_btree::AMI_btree(const AMI_btree_params ¶ms): header_(), params_(params), + status_(AMI_BTREE_STATUS_VALID) { + + char *base_name = tpie_tempnam("AMI_BTREE"); + name_ = base_name; + shared_init(base_name, AMI_WRITE_COLLECTION); + if (status_ == AMI_BTREE_STATUS_VALID) { + persist(PERSIST_DELETE); + } +} + +//// *AMI_btree::AMI_btree* //// +template +AMI_btree::AMI_btree(const char *base_file_name, AMI_collection_type type, + const AMI_btree_params ¶ms): + header_(), params_(params), status_(AMI_BTREE_STATUS_VALID), stats_(), kov_(), name_(base_file_name) { + + shared_init(base_file_name, type); + + if (status_ == AMI_BTREE_STATUS_VALID) { + if (pcoll_leaves_->size() > 0) { + // Read root bid, height and size from header. + header_ = *((header_t *) pcoll_nodes_->user_data()); + // TODO: sanity checks. + } + persist(PERSIST_PERSISTENT); + } +} + +//// *AMI_btree::AMI_btree* //// +template +AMI_btree::AMI_btree(const string& base_file_name, AMI_collection_type type, + const AMI_btree_params ¶ms): + header_(), params_(params), status_(AMI_BTREE_STATUS_VALID), stats_(), kov_(), name_(base_file_name) { + + shared_init(base_file_name.c_str(), type); + + if (status_ == AMI_BTREE_STATUS_VALID) { + if (pcoll_leaves_->size() > 0) { + // Read root bid, height and size from header. + header_ = *((header_t *) pcoll_nodes_->user_data()); + // TODO: sanity checks. + } + persist(PERSIST_PERSISTENT); + } +} + +//// *AMI_btree::shared_init* //// +template +void AMI_btree::shared_init(const char* base_file_name, AMI_collection_type type) { + + if (base_file_name == NULL) { + status_ = AMI_BTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("AMI_btree::AMI_btree: NULL file name."); + return; + } + +#define PATH_NAME_LENGTH 128 + + char lcollname[PATH_NAME_LENGTH]; + char ncollname[PATH_NAME_LENGTH]; + strncpy(lcollname, base_file_name, PATH_NAME_LENGTH - 2); + strncpy(ncollname, base_file_name, PATH_NAME_LENGTH - 2); + strcat(lcollname, ".l"); + strcat(ncollname, ".n"); + // Initialize these pointers to NULL to avoid errors in the + // destructor in case of premature return from this function. + node_cache_ = NULL; + leaf_cache_ = NULL; + pcoll_leaves_ = NULL; + pcoll_nodes_ = NULL; + + pcoll_leaves_ = new collection_t(lcollname, type, params_.leaf_block_factor); + if (!pcoll_leaves_->is_valid()) { + status_ = AMI_BTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("AMI_btree::AMI_btree: Could not open leaves collection."); + return; + } + + pcoll_nodes_ = new collection_t(ncollname, type, params_.node_block_factor); + if (!pcoll_nodes_->is_valid()) { + status_ = AMI_BTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("AMI_btree::AMI_btree: Could not open nodes collection."); + return; + } + + // Initialize the caches (associativity = 8). + node_cache_ = new node_cache_t(params_.node_cache_size, 8); + leaf_cache_ = new leaf_cache_t(params_.leaf_cache_size, 8); + + // Give meaningful values to parameters, if necessary. + size_t leaf_capacity = AMI_BTREE_LEAF::el_capacity(pcoll_leaves_->block_size()); + if (params_.leaf_size_max == 0 || params_.leaf_size_max > leaf_capacity) + params_.leaf_size_max = leaf_capacity; + if (params_.leaf_size_max == 1) + params_.leaf_size_max = 2; + + if (params_.leaf_size_min == 0) + params_.leaf_size_min = params_.leaf_size_max / 2; + + size_t node_capacity = AMI_BTREE_NODE::el_capacity(pcoll_nodes_->block_size()); + if (params_.node_size_max == 0 || params_.node_size_max > node_capacity) + params_.node_size_max = node_capacity; + if (params_.node_size_max == 1 || params_.node_size_max == 2) + params_.node_size_max = 3; + + if (params_.node_size_min == 0) + params_.node_size_min = params_.node_size_max / 2; + + // Set the right block factor parameters for the case of an existing tree. + params_.leaf_block_factor = pcoll_leaves_->block_factor(); + params_.node_block_factor = pcoll_nodes_->block_factor(); +} + + +//// *AMI_btree::sort* //// +template +AMI_err AMI_btree::sort(AMI_STREAM* in_stream, AMI_STREAM* &out_stream) { + + if (status_ != AMI_BTREE_STATUS_VALID) { + TP_LOG_FATAL_ID("sort: tree is invalid."); + return AMI_ERROR_GENERIC_ERROR; + } + if (in_stream == NULL) { + TP_LOG_FATAL_ID("sort: attempting to sort a NULL stream pointer."); + return AMI_ERROR_GENERIC_ERROR; + } + if (in_stream->stream_len() == 0) { + TP_LOG_FATAL_ID("sort: attempting to sort an empty stream."); + return AMI_ERROR_GENERIC_ERROR; + } + + AMI_err err; + comp_for_sort cmp; + + if (out_stream == NULL) { + out_stream = new AMI_STREAM; + if (!out_stream->is_valid()) { + TP_LOG_FATAL_ID("sort: error initializing temporary stream."); + delete out_stream; + return AMI_ERROR_OBJECT_INITIALIZATION; + } + out_stream->persist(PERSIST_DELETE); + } + + err = AMI_sort(in_stream, out_stream, &cmp); + + if (err != AMI_ERROR_NO_ERROR) + TP_LOG_WARNING_ID("sort: sorting returned error."); + else if (in_stream->stream_len() != out_stream->stream_len()) { + TP_LOG_WARNING_ID("sort: sorted stream has different length than unsorted stream."); + err = AMI_ERROR_GENERIC_ERROR; + } + + return err; +} + +//// *AMI_btree::load_sorted* //// +template +AMI_err AMI_btree::load_sorted(AMI_STREAM* s, float leaf_fill, float node_fill) { + + if (status_ != AMI_BTREE_STATUS_VALID) { + TP_LOG_FATAL_ID("load: tree is invalid."); + return AMI_ERROR_GENERIC_ERROR; + } + if (s == NULL) { + TP_LOG_FATAL_ID("load: attempting to load with NULL stream pointer."); + return AMI_ERROR_GENERIC_ERROR; + } + if (!s->is_valid()) { + TP_LOG_FATAL_ID("load: attempting to load with invalid input stream."); + return AMI_ERROR_GENERIC_ERROR; + } + + Value* pv; + AMI_err err = AMI_ERROR_NO_ERROR; + AMI_btree_params params_saved = params_; + params_.leaf_size_max = std::min(params_.leaf_size_max, size_t(leaf_fill*params_.leaf_size_max)); + params_.node_size_max = std::min(params_.node_size_max, size_t(node_fill*params_.node_size_max)); + + AMI_BTREE_LEAF* lcl = NULL; // locally cached leaf. + + err = s->seek(0); + assert(err == AMI_ERROR_NO_ERROR); + + // Repeatedly insert items in sorted order. + while ((err = s->read_item(&pv)) == AMI_ERROR_NO_ERROR) { + insert_load(*pv, lcl); + } + + if (err != AMI_ERROR_END_OF_STREAM) + TP_LOG_FATAL_ID("load: error occured while reading the input stream."); + else + err = AMI_ERROR_NO_ERROR; + + if (lcl != NULL) + release_leaf(lcl); + params_ = params_saved; + + return err; +} + +//// *AMI_btree::load* //// +template +AMI_err AMI_btree::load(AMI_STREAM* s, float leaf_fill, float node_fill) { + + AMI_err err; + AMI_STREAM* stream_s = new AMI_STREAM; + + err = sort(s, stream_s); + + if (err != AMI_ERROR_NO_ERROR) + return err; + + err = load_sorted(stream_s, leaf_fill, node_fill); + + delete stream_s; + return err; +} + +//// *AMI_btree::unload* //// +template +AMI_err AMI_btree::unload(AMI_STREAM* s) { + + if (status_ != AMI_BTREE_STATUS_VALID) { + TP_LOG_WARNING_ID("unload: tree is invalid. unload aborted."); + return AMI_ERROR_GENERIC_ERROR; + } + if (s == NULL) { + TP_LOG_WARNING_ID("unload: NULL stream pointer. unload aborted."); + return AMI_ERROR_GENERIC_ERROR; + } + + AMI_bid lbid = find_min_leaf(); + AMI_BTREE_LEAF* l; + AMI_err err = AMI_ERROR_NO_ERROR; + size_t i; + + tp_assert(lbid != 0, ""); + + while (lbid != 0) { + l = fetch_leaf(lbid); + for (i = 0; i < l->size(); i++) + s->write_item(l->el[i]); + lbid = l->next(); + release_leaf(l); + } + return err; +} + +//// *AMI_btree::load* //// +template +AMI_err AMI_btree::load(AMI_BTREE* bt, float leaf_fill, float node_fill) { + + if (!is_valid()) { + TP_LOG_WARNING_ID("load: tree is invalid."); + return AMI_ERROR_GENERIC_ERROR; + } + if (bt == NULL) { + TP_LOG_WARNING_ID("load: NULL btree pointer."); + return AMI_ERROR_GENERIC_ERROR; + } + if (!bt->is_valid()) { + TP_LOG_WARNING_ID("load: input tree is invalid."); + return AMI_ERROR_GENERIC_ERROR; + } + + AMI_btree_params params_saved = params_; + AMI_err err = AMI_ERROR_NO_ERROR; + params_.leaf_size_max = std::min(params_.leaf_size_max, size_t(leaf_fill*params_.leaf_size_max)); + params_.node_size_max = min(params_.leaf_size_max, size_t(node_fill*params_.node_size_max)); + AMI_BTREE_LEAF* lcl = NULL; // locally cached leaf. + + // Get the bid of the min leaf in bt. + AMI_bid lbid = bt->find_min_leaf(); + // Pointer to a leaf in bt. + AMI_BTREE_LEAF* btl; + size_t i; + + tp_assert(lbid != 0, ""); + + // Iterate over all leaves of bt. + while (lbid != 0) { + btl = bt->fetch_leaf(lbid); + + for (i = 0; i < btl->size(); i++) { + insert_load(btl->el[i], lcl); + } + + // Get next leaf in bt. + lbid = btl->next(); + + bt->release_leaf(btl); + } + + if (lcl != NULL) + release_leaf(lcl); + params_ = params_saved; + return err; +} + + +//// *AMI_btree::dfs_preorder* //// +template +pair AMI_btree::dfs_preorder(int& level) { + + Key k; + if (level == -1) { + // Empty the stack. This allows restarts in the middle of a + // traversal. All previous state information is lost. + while (!dfs_stack_.empty()) + dfs_stack_.pop(); + // Push the root on the stack. + dfs_stack_.push(pair(header_.root_bid, 0)); + + level = (int)dfs_stack_.size() - 1; + return pair(header_.root_bid, k); + } else { + AMI_BTREE_NODE* bn; + AMI_bid id = 0; + // If the top of the stack is a node + if (dfs_stack_.size() < header_.height) { + // Fetch the node ... + bn = fetch_node(dfs_stack_.top().first); + // ... and get the appropriate child. + id = bn->lk[dfs_stack_.top().second]; + dfs_stack_.push(pair(id, 0)); + release_node(bn); + } else { // top of the stack is leaf + dfs_stack_.pop(); + bool done = false; + while (!dfs_stack_.empty() && !done) { + // Fetch the node ... + bn = fetch_node(dfs_stack_.top().first); + // Increment the link index. + (dfs_stack_.top().second)++; + // Check the link index for validity. + if (dfs_stack_.top().second < bn->size() + 1) { + id = bn->lk[dfs_stack_.top().second]; + k = bn->el[dfs_stack_.top().second-1]; + dfs_stack_.push(pair(id, 0)); + done = true; + } else + dfs_stack_.pop(); + + release_node(bn); + } + } + level = (int)dfs_stack_.size() - 1; + return pair(id, k); + } +} + +//// *AMI_btree::find* //// +template +bool AMI_btree::find(const Key& k, Value& v) { + + bool ans; + size_t idx; + + if (header_.height == 0) + return false; + + // Find the leaf that might contain the key and fetch it. + AMI_bid bid = find_leaf(k); + AMI_BTREE_LEAF *p = fetch_leaf(bid); + + // Check whether we have a match. + idx = p->find(k); + + if (idx < p->size() && + !comp_(kov_(p->el[idx]), k) && + !comp_(k, kov_(p->el[idx]))) { + v = p->el[idx]; // using Value's assignment operator. + ans = true; + } else + ans = false; + + // Write back the leaf and empty the stack. + release_leaf(p); + empty_stack(); + + return ans; +} + +//// *AMI_btree::pred* //// +template +bool AMI_btree::pred(const Key& k, Value& v) { + + bool ans = false; + AMI_BTREE_LEAF * pl; + AMI_bid bid; + size_t idx; + + assert(header_.height >= 1); + assert(path_stack_.empty()); + + // Get a close candidate and path_stack + bid = find_leaf(k); + pl = fetch_leaf(bid); + idx = pl->pred(k); + + // Check whether we have a match. + if (comp_(kov_(pl->el[idx]),k)){ + v = pl->el[idx]; + ans = true; + } else { +#if AMI_BTREE_LEAF_PREV_POINTER + bid = pl->prev(); +#else + assert(0); +#endif + if (bid != 0) { + release_leaf(pl); + pl = fetch_leaf(bid); + v = pl->el[pl->pred(k)]; + ans=true; + } + } + + // Write back the leaf and empty the stack. + release_leaf(pl); + empty_stack(); + + return ans; +} + +//// *AMI_btree::succ* //// +template +bool AMI_btree::succ(const Key& k, Value& v) { + + bool ans = false; + AMI_BTREE_LEAF * pl; + AMI_bid bid; + size_t idx; + + assert(header_.height >= 1); + assert(path_stack_.empty()); + + // Get a close candidate and path_stack + bid = find_leaf(k); + pl = fetch_leaf(bid); + idx = pl->succ(k); + + // Check whether we have a match. + if (comp_(k,kov_(pl->el[idx]))){ + v = pl->el[idx]; + ans = true; + } else { + bid = pl->next(); + if (bid !=0) { + release_leaf(pl); + pl = fetch_leaf(bid); + v = pl->el[pl->succ(k)]; + ans=true; + } + } + + // Write back the leaf and empty the stack. + release_leaf(pl); + empty_stack(); + + return ans; +} + +//// *AMI_btree::insert* //// +template +bool AMI_btree::insert(const Value& v) { + + bool ans = true; + + // Check for empty tree. + if (header_.height == 0) { + return insert_empty(v); + } + + // Find the leaf where v should be inserted and fetch it. + AMI_bid bid = find_leaf(kov_(v)); + AMI_BTREE_LEAF *p = fetch_leaf(bid); + + // If the leaf is not full, insert v into it. + if (!full_leaf(p)) { + ans = p->insert(v); + release_leaf(p); + } else { +#if AMI_BTREE_UNIQUE_KEYS + size_t pos = p->find(kov_(v)); + // Check for duplicate key. + if (pos < p->size() && + !comp_(kov_(v), kov_(p->el[pos])) && + !comp_(kov_(p->el[pos]), kov_(v))) { + TP_LOG_WARNING_ID("Attempting to insert duplicate key. Ignoring insert."); + ans = false; + } else { + ans = insert_split(v, p, bid); + } +#else + ans = insert_split(v, p, bid); +#endif + } + + empty_stack(); + + // Update the size and return. + header_.size += ans ? 1: 0; + return ans; +} + +//// *AMI_btree::modify* //// +template +bool AMI_btree::modify(const Value& v) { + + bool ans = true; + + // Check for empty tree. + if (header_.height == 0) { + return insert_empty(v); + } + + // Find the leaf where v should be inserted and fetch it. + AMI_bid bid = find_leaf(kov_(v)); + AMI_BTREE_LEAF *p = fetch_leaf(bid); + + if(p->erase(kov_(v))){ + // Item was present, can insert without overflow + ans = p->insert(v); + release_leaf(p); + } + else{ + // Item was not present, do a standard insert + // If the leaf is not full, insert v into it. + if (!full_leaf(p)) { + ans = p->insert(v); + release_leaf(p); + } + else { +#if AMI_BTREE_UNIQUE_KEYS + size_t pos = p->find(kov_(v)); + // Check for duplicate key. + if (pos < p->size() && + !comp_(kov_(v), kov_(p->el[pos])) && + !comp_(kov_(p->el[pos]), kov_(v))) { + TP_LOG_WARNING_ID("Attempting to insert duplicate key. Ignoring insert."); + ans = false; + } + else { + ans = insert_split(v, p, bid); + } +#else + ans = insert_split(v, p, bid); +#endif + } + } + + empty_stack(); + + // Return answer. + return ans; +} + +//// *AMI_btree::insert_load* //// +template +inline bool AMI_btree::insert_load(const Value& v, AMI_BTREE_LEAF* &lcl) { + + AMI_BTREE_LEAF *p; + bool ans = false; + AMI_bid bid; + + // Check for empty tree. + if (header_.height == 0) { + ans = insert_empty(v); + lcl = fetch_leaf(header_.root_bid); + return ans; + } + + p = lcl; + // Verify sorting. + //// assert(!comp_(kov_(v), kov_(lcl->el[lcl->size()-1]))); + + if (!comp_(kov_(p->el[p->size()-1]), kov_(v))) + ans = false; + else { + // If the leaf is not full, insert v into it. + if (!full_leaf(p)) { + ans = p->insert(v); + } else { + //AMI_bid pbid = p->bid();//// + release_leaf(p); + + // Do the whole routine. + bid = find_leaf(kov_(v)); + //assert(bid == pbid);//// + // Should be in cache. + p = fetch_leaf(bid); + // bid will store the id of the leaf containing v after insert. + ans = insert_split(v, p, bid, true); + lcl = fetch_leaf(bid); + } + } + + empty_stack(); + + // Update the size and return. + header_.size += ans ? 1: 0; + return ans; +} + + +//// *AMI_btree::insert_empty* //// +template +bool AMI_btree::insert_empty(const Value& v) { + bool ans; + assert(header_.size == 0); + + // Create new root (as leaf). + AMI_BTREE_LEAF* lroot = fetch_leaf(); + + // Store its bid. + header_.root_bid = lroot->bid(); + + lroot->next() = 0; +#if AMI_BTREE_LEAF_PREV_POINTER + lroot->prev() = 0; +#endif + + // Insert v into the root. + ans = lroot->insert(v); + assert(ans); + + // Don't want the root object around. + release_leaf(lroot); + + // Height and size are now 1. + header_.height = 1; + header_.size = 1; + + status_ = AMI_BTREE_STATUS_VALID; + return ans; +} + +//// *AMI_btree::insert_split* //// +template +bool AMI_btree::insert_split(const Value& v, AMI_BTREE_LEAF* p, AMI_bid& leaf_id, bool loading) { + + AMI_BTREE_LEAF *q, *r; + pair top; + AMI_bid bid; + bool ans; + + // Split the leaf. + q = fetch_leaf(); + Key mid_key; + if (loading) { + mid_key = kov_(p->el[p->size()-1]); + } else + mid_key = p->split(*q); + + // Update the next pointers. + q->next() = p->next(); + p->next() = q->bid(); + +#if AMI_BTREE_LEAF_PREV_POINTER + // Update the prev pointers. + q->prev() = p->bid(); + if (q->next() != 0) { + r = fetch_leaf(q->next()); + r->prev() = q->bid(); + release_leaf(r); + } +#endif + + bid = q->bid(); + + // Insert in the appropriate leaf. + if (!comp_(mid_key, kov_(v)) && !comp_(kov_(v), mid_key)) { + ans = false; + TP_LOG_WARNING_ID("Attempting to insert duplicate key"); + // TODO: during loading, this is not enough. q may remain empty! + } else { + ans = (comp_(mid_key, kov_(v)) ? q: p)->insert(v); + leaf_id = (comp_(mid_key, kov_(v)) ? q: p)->bid(); + assert(!loading || q->size() == 1); + } + + release_leaf(p); + release_leaf(q); + + Key fmid_key; + AMI_BTREE_NODE *qq, *fq; + + // Go up the tree. + while (bid != 0 && !path_stack_.empty()) { + + // Pop the stack to find q's father. + top = path_stack_.top(); + path_stack_.pop(); + + // Read the father of q. + fq = fetch_node(top.first); + + // Check whether we need to go further up the tree. + if (!full_node(fq)) { + + // Insert the key and link into position. + fq->insert_pos(mid_key, bid, top.second, top.second + 1); + + // Exit the loop. + bid = 0; + + } else { // Need to split further. + + // Split fq. + qq = fetch_node(); + fmid_key = loading ? mid_key: fq->split(*qq); + + // Insert in the appropriate node. + if (loading) + qq->lk[0] = bid; // TODO: this is ugly. qq has no keys now. + else + (comp_(fmid_key, mid_key) ? qq: fq)->insert(mid_key, bid); + + // Prepare for next iteration. + mid_key = fmid_key; + bid = qq->bid(); + release_node(qq); + } + + release_node(fq); + + } // End of while. + + // Check whether the root was split. + if (bid != 0) { + + assert(path_stack_.empty()); + + // Create a new root node with the 2 links. + AMI_BTREE_NODE* nroot = fetch_node(); + // Not very nice... + nroot->lk[0] = header_.root_bid; + nroot->insert_pos(mid_key, bid, 0, 1); + + // Update the root id. + header_.root_bid = nroot->bid(); + + release_node(nroot); + + // Update the height. + header_.height++; + + } + return ans; +} + + +//// *AMI_btree::find_leaf* //// +template +AMI_bid AMI_btree::find_leaf(const Key& k) { + + AMI_BTREE_NODE * p; + AMI_bid bid = header_.root_bid; + size_t pos; + TPIE_OS_SIZE_T level; + + assert(header_.height >= 1); + assert(path_stack_.empty()); + + // Go down the tree. + for (level = header_.height - 1; level > 0; level--) { + // Fetch the node. + p = fetch_node(bid); + // Find the position of the link to the child node. + pos = p->find(k); + // Push the current node and position on the path stack. + path_stack_.push(pair(bid, pos)); + // Find the actual block id of the child node. + bid = p->lk[pos]; + // Release the node. + release_node(p); + } + + // This should be the id of a leaf. + return bid; +} + +//// *AMI_btree::find_min_leaf* //// +template +AMI_bid AMI_btree::find_min_leaf() { + AMI_BTREE_NODE* p; + AMI_bid bid = header_.root_bid; + int level; + + assert(header_.height >= 1); + + for (level = (int)header_.height - 1; level > 0; level--) { + p = fetch_node(bid); + bid = p->lk[0]; + release_node(p); + } + + return bid; +} + +//// *AMI_btree::underflow_leaf* //// +template +bool AMI_btree::underflow_leaf(AMI_BTREE_LEAF *p) const { + return p->size() <= cutoff_leaf(p); +} + +//// *AMI_btree::underflow_node* //// +template +bool AMI_btree::underflow_node(AMI_BTREE_NODE *p) const { + return p->size() <= cutoff_node(p); +} + +//// *AMI_btree::cutoff_leaf* //// +template +size_t AMI_btree::cutoff_leaf(AMI_BTREE_LEAF *p) const { + // Be careful how you test for the root (thanks, Andrew). + return (p->bid() == header_.root_bid && header_.height == 1) ? 0 + : params_.leaf_size_min - 1; +} + +//// *AMI_btree::cutoff_node* //// +template +size_t AMI_btree::cutoff_node(AMI_BTREE_NODE *p) const { + return (p->bid() == header_.root_bid) ? 0 : params_.node_size_min - 1; +} + +//// *AMI_btree::full_leaf* //// +template +bool AMI_btree::full_leaf(const AMI_BTREE_LEAF *p) const { + return (p->size() == params_.leaf_size_max); +} + +//// *AMI_btree::full_node* //// +template +bool AMI_btree::full_node(const AMI_BTREE_NODE *p) const { + return (p->size() == params_.node_size_max); +} + +//// *AMI_btree::balance_node* //// +template +bool AMI_btree::balance_node(AMI_BTREE_NODE *f, AMI_BTREE_NODE *p, size_t pos) { + + bool ans = false; + AMI_BTREE_NODE *sib; + + assert(p->bid() == f->lk[pos]); + + // First try to borrow from the right sibling. + if (pos < f->size()) { + + sib = fetch_node(f->lk[pos + 1]); + if (sib->size() >= cutoff_node(sib) + 2) { + + // Rotate left. Insert the key from the father (f) and the link + // from the sibling (sib) to the end of p. + p->insert_pos(f->el[pos], sib->lk[0], p->size(), p->size() + 1); + // Move the key from sib up to the father (f). + f->el[pos] = sib->el[0]; + // Remove the first key and link of sib. + sib->erase_pos(0, 0); + ans = true; + + } + release_node(sib); + } + + if (pos > 0 && !ans) { + + sib = fetch_node(f->lk[pos - 1]); + if (sib->size() >= cutoff_node(sib) + 2) { + // Rotate right. + p->insert_pos(f->el[pos - 1], sib->lk[sib->size()], 0, 0); + f->el[pos - 1] = sib->el[sib->size() - 1]; + sib->erase_pos(sib->size() - 1, sib->size()); + ans = true; + + } + release_node(sib); + } + + // Return. + return ans; +} + +//// *AMI_btree::balance_leaf* //// +template +bool AMI_btree::balance_leaf(AMI_BTREE_NODE *f, AMI_BTREE_LEAF *p, size_t pos) { + + bool ans = false; + AMI_BTREE_LEAF *sib; + + // First try to borrow from the right sibling. + if (pos < f->size()) { + sib = fetch_leaf(f->lk[pos + 1]); + if (sib->size() >= cutoff_leaf(sib) + 2) { + +#if (!AMI_BTREE_LEAF_ELEMENTS_SORTED) + sib->sort(); +#endif + // Rotate left. + // Insert the first element from sib to the end of p. + p->insert(sib->el[0]); + // Update the key in the father (f). + f->el[pos] = kov_(sib->el[0]); + // Delete the first element from sib. + sib->erase_pos(0); + ans = true; + + } + release_leaf(sib); + } + + if (pos > 0 && !ans) { + + sib = fetch_leaf(f->lk[pos - 1]); + if (sib->size() >= cutoff_leaf(sib) + 2) { + +#if (!AMI_BTREE_LEAF_ELEMENTS_SORTED) + sib->sort(); +#endif + // Rotate right. + // Insert the last element of sib to the beginning of p. + p->insert(sib->el[sib->size() - 1]); + // Update the key in the father. + f->el[pos - 1] = kov_(sib->el[sib->size() - 2]); + // Delete the last element from sib. + sib->erase_pos(sib->size() - 1); + ans = true; + + } + release_leaf(sib); + } + + return ans; +} + +//// *AMI_btree::merge_leaf* //// +template +void AMI_btree::merge_leaf(AMI_BTREE_NODE* f, AMI_BTREE_LEAF* &p, size_t pos) { + + AMI_BTREE_LEAF * sib, *r; + + // f will be the father of both p and sib. + + if (pos < f->size()) { + + // Merge with right sibling. + // Fetch the sibling. + sib = fetch_leaf(f->lk[pos + 1]); + // Update the next pointer. + p->next() = sib->next(); +#if AMI_BTREE_LEAF_PREV_POINTER + // Update the prev pointer. + if (p->next() != 0) { + r = fetch_leaf(p->next()); + r->prev() = p->bid(); + release_leaf(r); + } +#endif + // Do the merge. + p->merge(*sib); + // Delete the sibling. + sib->persist(PERSIST_DELETE); + release_leaf(sib); + // Delete the entry for the sibling from the father. + f->erase_pos(pos, pos + 1); + + } else { + + // Merge with left sibling. + sib = fetch_leaf(f->lk[pos - 1]); + sib->next() = p->next(); +#if AMI_BTREE_LEAF_PREV_POINTER + if (sib->next() != 0) { + r = fetch_leaf(sib->next()); + r->prev() = sib->bid(); + release_leaf(r); + } +#endif + sib->merge(*p); + p->persist(PERSIST_DELETE); + release_leaf(p); + f->erase_pos(pos - 1, pos); + p = sib; + } + +} + +//// *AMI_btree::merge_node* //// +template +void AMI_btree::merge_node(AMI_BTREE_NODE* f, AMI_BTREE_NODE* &p, size_t pos) { + + AMI_BTREE_NODE * sib; + + // f will be the father of both p and sib. + + if (pos < f->size()) { + sib = fetch_node(f->lk[pos + 1]); + p->merge(*sib, f->el[pos]); + // Delete the sibling. + sib->persist(PERSIST_DELETE); + release_node(sib); + f->erase_pos(pos, pos + 1); + } else { + sib = fetch_node(f->lk[pos - 1]); + sib->merge(*p, f->el[pos - 1]); + p->persist(PERSIST_DELETE); + release_node(p); + f->erase_pos(pos - 1, pos); + p = sib; + } + +} + +//// *AMI_btree::erase* //// +template +bool AMI_btree::erase(const Key& k) { + + bool ans; + + if (header_.height == 0) + return false; + + // Find the leaf where the data might be and fetch it. + AMI_bid bid = find_leaf(k); + AMI_BTREE_LEAF *p = fetch_leaf(bid); + + // Check for exact match and delete. + ans = p->erase(k); + + // Sanity check. + assert(ans || !underflow_leaf(p)); + + // Update the size. + header_.size -= ans ? 1: 0; + + if (!underflow_leaf(p)) { + // No underflow. Cleanup and exit. + release_leaf(p); + empty_stack(); + return ans; + } + + AMI_BTREE_NODE * q; + pair top; + + // Underflow. Balance or merge up the tree. + // Treat the first iteration separately since it deals with leaves. + if (!path_stack_.empty()) { + + // Pop the father of p from the stack. + top = path_stack_.top(); + path_stack_.pop(); + + // Load the father of p; + q = fetch_node(top.first); + + // Can we borrow an element from a sibling? + if (balance_leaf(q, p, top.second)) { + bid = 0; // Done. + } else { + + // Merge p with a sibling. + merge_leaf(q, p, top.second); + + // Check for underflow in the father. + bid = (underflow_node(q) ? q->bid() : 0); + } + + // Prepare for next iteration (or exit). + release_leaf(p); + } + + AMI_BTREE_NODE * pp = q; + + // The rest of the iterations up the tree. + while (!path_stack_.empty() && bid != 0) { + + // Find the father of p. + top = path_stack_.top(); + path_stack_.pop(); + + // Load the father of p; + q = fetch_node(top.first); + + // Try to balance p by borrowing from sibling(s). + if (balance_node(q, pp, top.second)) { + + bid = 0; + + } else { + + // Merge p with right sibling. + merge_node(q, pp, top.second); + + // Check for underflow in the father. + bid = (underflow_node(q) ? q->bid() : 0); + } + + // Prepare for next iteration (or exit). + release_node(pp); + pp = q; + + } // end of while.. + + // Check for root underflow. + if (bid != 0) { + + assert(path_stack_.empty()); + assert(pp->bid() == header_.root_bid); + + // New root. + header_.root_bid = pp->lk[0]; + + // Remove old root from collection. + pp->persist(PERSIST_DELETE); + + header_.height--; + } + + release_node(pp); + + // Empty the path stack and return. + empty_stack(); + return ans; +} + +template +void AMI_btree::persist(persistence per) { + pcoll_leaves_->persist(per); + pcoll_nodes_->persist(per); +} + +template +AMI_btree::~AMI_btree() { + if (status_ == AMI_BTREE_STATUS_VALID) { + // Write initialization info into the pcoll_nodes_ header. + *((header_t *) pcoll_nodes_->user_data()) = header_; + } + delete node_cache_; + delete leaf_cache_; + + // Delete the two collections. + delete pcoll_leaves_; + delete pcoll_nodes_; +} + +template +AMI_BTREE_NODE* AMI_btree::fetch_node(AMI_bid bid) { + AMI_BTREE_NODE* q; + stats_.record(NODE_FETCH); + // Warning: using short-circuit evaluation. Order is important. + if ((bid == 0) || !node_cache_->read(bid, q)) { + q = new AMI_BTREE_NODE(pcoll_nodes_, bid); + } + return q; +} + +template +AMI_BTREE_LEAF* AMI_btree::fetch_leaf(AMI_bid bid) { + AMI_BTREE_LEAF* q; + stats_.record(LEAF_FETCH); + // Warning: using short-circuit evaluation. Order is important. + if ((bid == 0) || !leaf_cache_->read(bid, q)) { + q = new AMI_BTREE_LEAF(pcoll_leaves_, bid); + } + return q; +} + +template +void AMI_btree::release_node(AMI_BTREE_NODE *p) { + stats_.record(NODE_RELEASE); + if (p->persist() == PERSIST_DELETE) + delete p; + else + node_cache_->write(p->bid(), p); +} + +template +void AMI_btree::release_leaf(AMI_BTREE_LEAF *p) { + stats_.record(LEAF_RELEASE); + if (p->persist() == PERSIST_DELETE) + delete p; + else + leaf_cache_->write(p->bid(), p); +} + +template +const tpie_stats_tree& AMI_btree::stats() { + node_cache_->flush(); + leaf_cache_->flush(); + stats_.set(LEAF_READ, pcoll_leaves_->stats().get(BLOCK_GET)); + stats_.set(LEAF_WRITE, pcoll_leaves_->stats().get(BLOCK_PUT)); + stats_.set(LEAF_CREATE, pcoll_leaves_->stats().get(BLOCK_NEW)); + stats_.set(LEAF_DELETE, pcoll_leaves_->stats().get(BLOCK_DELETE)); + stats_.set(LEAF_COUNT, pcoll_leaves_->size()); + stats_.set(NODE_READ, pcoll_nodes_->stats().get(BLOCK_GET)); + stats_.set(NODE_WRITE, pcoll_nodes_->stats().get(BLOCK_PUT)); + stats_.set(NODE_CREATE, pcoll_nodes_->stats().get(BLOCK_NEW)); + stats_.set(NODE_DELETE, pcoll_nodes_->stats().get(BLOCK_DELETE)); + stats_.set(NODE_COUNT, pcoll_nodes_->size()); + return stats_; +} + +// Undefine shortcuts. +#undef AMI_BTREE_NODE +#undef AMI_BTREE_LEAF +#undef AMI_BTREE + +#endif // _AMI_BTREE_H diff --git a/fastlib/u/nvasil/tpie/ami_cache.h b/fastlib/u/nvasil/tpie/ami_cache.h new file mode 100644 index 0000000000..473e81c88b --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_cache.h @@ -0,0 +1,238 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: ami_cache.h +// Author: Octavian Procopiuc +// +// $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 +// Get the logging macros. +#include +// Get the b_vector class. +#include + +// 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 AMI_cache_manager_lru: public AMI_cache_manager_base { +protected: + + typedef pair 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 +AMI_cache_manager_lru::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 +inline bool AMI_cache_manager_lru::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 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 +inline bool AMI_cache_manager_lru::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 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 +bool AMI_cache_manager_lru::erase(TPIE_OS_OFFSET k) { + + TPIE_OS_SIZE_T i; + assert(k != 0); + + // The cache line, based on the key k. + b_vector 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 +void AMI_cache_manager_lru::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 +AMI_cache_manager_lru::~AMI_cache_manager_lru() { + flush(); + if (capacity_ > 0) { + delete [] pdata_; + } +} + +#endif // _AMI_CACHE_H diff --git a/fastlib/u/nvasil/tpie/ami_coll.h b/fastlib/u/nvasil/tpie/ami_coll.h new file mode 100644 index 0000000000..09f178abe0 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_coll.h @@ -0,0 +1,27 @@ +// +// File: ami_coll.h +// Author: Octavian Procopiuc +// +// $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 + +#include +#include + +// 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 diff --git a/fastlib/u/nvasil/tpie/ami_coll_base.h b/fastlib/u/nvasil/tpie/ami_coll_base.h new file mode 100644 index 0000000000..1c2acadae7 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_coll_base.h @@ -0,0 +1,29 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: ami_coll_base.h +// Author: Octavian Procopiuc +// +// $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 + +// 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 diff --git a/fastlib/u/nvasil/tpie/ami_coll_single.h b/fastlib/u/nvasil/tpie/ami_coll_single.h new file mode 100644 index 0000000000..5874d056d6 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_coll_single.h @@ -0,0 +1,117 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: ami_coll_single.h +// Author: Octavian Procopiuc +// +// $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 + +// For persist type. +#include +// Get an appropriate BTE collection. +#include +// For AMI_collection_type and AMI_collection_status. +#include +// The tpie_tempnam() function. +#include +// Get the tpie_stats_coll class for collection statistics. +#include + +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 +AMI_collection_single::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 +AMI_collection_single::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 diff --git a/fastlib/u/nvasil/tpie/ami_device.cc b/fastlib/u/nvasil/tpie/ami_device.cc new file mode 100644 index 0000000000..c7b7b4ba4d --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_device.cc @@ -0,0 +1,146 @@ +// +// File: ami_device.cpp +// Author: Darren Erik Vengroff +// 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 +#include + +#include +#include + + +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; +} diff --git a/fastlib/u/nvasil/tpie/ami_device.h b/fastlib/u/nvasil/tpie/ami_device.h new file mode 100644 index 0000000000..0f9a1e30bd --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_device.h @@ -0,0 +1,38 @@ +// Copyright (c) 1993 Darren Erik Vengroff +// +// File: ami_device.h +// Author: Darren Erik Vengroff +// 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 + +#include + +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 + diff --git a/fastlib/u/nvasil/tpie/ami_err.h b/fastlib/u/nvasil/tpie/ami_err.h new file mode 100644 index 0000000000..eedac5a117 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_err.h @@ -0,0 +1,55 @@ +// +// File: ami_err.h +// Author: Octavian Procopiuc +// (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 diff --git a/fastlib/u/nvasil/tpie/ami_gen_perm.h b/fastlib/u/nvasil/tpie/ami_gen_perm.h new file mode 100644 index 0000000000..fb7b543950 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_gen_perm.h @@ -0,0 +1,141 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: ami_gen_perm.h +// Author: Darren Vengroff +// 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 +// Get AMI_scan_object. +#include +// Get AMI_sort +#include + +#include + +// (tavi) moved dest_obj definition down due to error in gcc 2.8.1 +template class dest_obj; + +// A comparison operator that simply compares destinations (for sorting). +template +int operator<(const dest_obj &s, const dest_obj &t) +{ + return s.dest < t.dest; +} + +template +int operator>(const dest_obj &s, const dest_obj &t) +{ + return s.dest > t.dest; +} + + +template +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 *out, + AMI_SCAN_FLAG *sfout) + { + if (!(*sfout = *sfin)) { + return AMI_SCAN_DONE; + } + *out = dest_obj(in, pgp->destination(input_offset++)); + return AMI_SCAN_CONTINUE; + } +}; + + +template +class gen_perm_strip_dest : AMI_scan_object { +public: + AMI_err initialize(void) { return AMI_ERROR_NO_ERROR; }; + AMI_err operate(const dest_obj &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 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 &s, const dest_obj &t); + friend int operator> <> (const dest_obj &s, const dest_obj &t); +//#else +// friend int operator< (const dest_obj &s, const dest_obj &t); +// friend int operator> (const dest_obj &s, const dest_obj &t); +//#endif + friend AMI_err gen_perm_strip_dest::operate(const dest_obj &in, + AMI_SCAN_FLAG *sfin, T *out, + AMI_SCAN_FLAG *sfout); +}; + + +template +AMI_err AMI_general_permute(AMI_STREAM *instream, AMI_STREAM *outstream, + AMI_gen_perm_object *gpo) { + + AMI_err ae; + gen_perm_add_dest gpad(gpo); + gen_perm_strip_dest gpsd; + AMI_STREAM< dest_obj > sdo_in; + AMI_STREAM< dest_obj > 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 *)instream, &gpad, + (AMI_STREAM< dest_obj > *)&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 > *)&sdo_out, &gpsd, + (AMI_STREAM *)outstream); + if (ae != AMI_ERROR_NO_ERROR) { + return ae; + } + + return AMI_ERROR_NO_ERROR; +} + +#endif // _AMI_GEN_PERM_H diff --git a/fastlib/u/nvasil/tpie/ami_gen_perm_object.h b/fastlib/u/nvasil/tpie/ami_gen_perm_object.h new file mode 100644 index 0000000000..a5b118167e --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_gen_perm_object.h @@ -0,0 +1,24 @@ +// +// File: ami_gen_perm_object.h +// Author: Darren Vengroff +// 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 + +// For AMI_err. +#include + +// 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 diff --git a/fastlib/u/nvasil/tpie/ami_kb_dist.h b/fastlib/u/nvasil/tpie/ami_kb_dist.h new file mode 100644 index 0000000000..47a04af1ec --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_kb_dist.h @@ -0,0 +1,242 @@ +// Copyright (c) 1995 Darren Erik Vengroff +// +// File: ami_kb_dist.h +// Author: Darren Erik Vengroff +// 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 + +// 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 +#include + +// This is a hack. The reason it is here is that if AMI_STREAM +// 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 type_amisc; +#endif + +template +AMI_err _AMI_KB_DIST(KB_KEY)(AMI_STREAM &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 *) + 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 **out_streams = new AMI_STREAM *[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; + + 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) diff --git a/fastlib/u/nvasil/tpie/ami_kb_sort.h b/fastlib/u/nvasil/tpie/ami_kb_sort.h new file mode 100644 index 0000000000..19ae5d6ae0 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_kb_sort.h @@ -0,0 +1,519 @@ +// Copyright (c) 1995 Darren Erik Vengroff +// +// File: ami_kb_sort.h +// Author: Darren Erik Vengroff +// 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 + +// Get definitions for working with Unix and Windows +#include + +#include +#include + +#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 AMI_bucket_list_elem +{ +public: + T data; + AMI_bucket_list_elem *next; + AMI_bucket_list_elem() : next(0) {}; + ~AMI_bucket_list_elem() {}; +}; + +#endif + + + +template +AMI_err _AMI_MM_KB_SORT(KB_KEY)(AMI_STREAM &instream, + AMI_STREAM &outstream, + const key_range &range); + + +template +AMI_err _AMI_KB_SORT(KB_KEY)(AMI_STREAM &instream, + AMI_STREAM &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 *) + + sizeof(AMI_bucket_list_elem))) { + 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 *name_stream, *name_stream2 = NULL; + + name_stream = new AMI_STREAM; + + // 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 *) + + sizeof(AMI_bucket_list_elem))) > + 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; + + // 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 *) + + sizeof(AMI_bucket_list_elem)) > + 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 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 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 +AMI_err _AMI_MM_KB_SORT(KB_KEY)(AMI_STREAM &instream, + AMI_STREAM &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 *) + + sizeof(AMI_bucket_list_elem))) { + cerr << '\n' << (TPIE_OS_LONGLONG)sz_avail << ' ' << (TPIE_OS_LONGLONG)stream_len << '\n'; + cerr << sizeof(T) << ' ' << sizeof(AMI_bucket_list_elem *) << + ' ' << sizeof(AMI_bucket_list_elem); + + 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 **buckets = + new AMI_bucket_list_elem*[(TPIE_OS_SIZE_T)stream_len]; + + AMI_bucket_list_elem *list_space = + new AMI_bucket_list_elem[(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 *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) diff --git a/fastlib/u/nvasil/tpie/ami_kd_base.h b/fastlib/u/nvasil/tpie/ami_kd_base.h new file mode 100644 index 0000000000..f0ff61dea1 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_kd_base.h @@ -0,0 +1,556 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: ami_kdtree_base.h +// Author: Octavian Procopiuc +// +// 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 +// For min, max. +#include +#include +#include + +// 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 AMI_kdtree_bin_node_base { +public: + + void initialize(const AMI_point &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 &p) const { + return (p[discr_dim_] < discr_val_) ? -1: (p[discr_dim_] > discr_val_) ? 1: 0; + } + // int discriminate(const AMI_point &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 AMI_kdtree_bin_node_default: public AMI_kdtree_bin_node_base { +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 AMI_kdtree_bin_node_short: public AMI_kdtree_bin_node_base { +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 AMI_kdtree_bin_node_small: public AMI_kdtree_bin_node_base { +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 AMI_kdtree_bin_node_large { +public: + void initialize(const AMI_point &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 &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 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 region_t { +protected: + // The low and high coordinates. The boolean bit is true iff the + // box is bounded on that dimension. + // pair lo_[dim]; + // pair 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& p1, const AMI_point& 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 point_lo() const { + AMI_point p; + for (TPIE_OS_SIZE_T i = 0; i < dim; i++) + p[i] = lo_[i]; + return p; + } + + AMI_point point_hi() const { + AMI_point 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& 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& 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 kdb_item_t { +public: + // For this purpose, every interval in region is considered open on + // the left and closed on the right. + region_t region; + link_type_t type; + AMI_bid bid; + kdb_item_t(const region_t& r, AMI_bid b, link_type_t t): + region(r), bid(b), type(t) {} + kdb_item_t() {} +} +#if !defined(_WIN32) + __attribute__((packed)) +#endif + ; + + +template +ostream &operator<<(ostream& s, const kdb_item_t& 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 +struct path_stack_item_t { + kdb_item_t item; + TPIE_OS_SIZE_T d; + TPIE_OS_SIZE_T el_idx; // + path_stack_item_t(const kdb_item_t& 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 diff --git a/fastlib/u/nvasil/tpie/ami_kdbtree.h b/fastlib/u/nvasil/tpie/ami_kdbtree.h new file mode 100644 index 0000000000..c2e13e1e26 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_kdbtree.h @@ -0,0 +1,1288 @@ +// Copyright (C) 2001,2002 Octavian Procopiuc +// +// File: ami_kdbtree.h +// Author: Octavian Procopiuc +// +// K-D-B-tree definition and implementation. +// +// $Id: ami_kdbtree.h,v 1.15 2005/01/27 20:42:11 tavi Exp $ +// + +#ifndef _AMI_KDBTREE_H +#define _AMI_KDBTREE_H + +#include +#include +#include +#include +#include +#include // STL string. + +#define AMI_KDBTREE_HEADER_MAGIC_NUMBER 0xA9542F + +// Forward declarations. +template class AMI_kdbtree_node; +template class AMI_kdbtree_leaf; + +// The AMI_kdbtree class. +template, class BTECOLL = BTE_COLLECTION > +class AMI_kdbtree { +public: + + typedef AMI_record point_t; + typedef AMI_record record_t; + typedef AMI_point key_t; + typedef AMI_STREAM stream_t; + typedef AMI_collection_single collection_t; + typedef AMI_kdbtree_node node_t; + typedef AMI_kdbtree_leaf leaf_t; + typedef kdb_item_t item_t; + + AMI_kdbtree(const char *base_file_name, AMI_collection_type type, + const AMI_kdbtree_params& params); + + AMI_kdbtree(const string& base_file_name, AMI_collection_type type, + const AMI_kdbtree_params& params); + + // Transform a kdtree into a kdbtree, in place. Returns true if + // succesful, false otherwise (i.e., the status is not + // AMI_KDBTREE_STATUS_KDTREE or the kdtree nodes contain too many keys). + bool kd2kdb(); + + TPIE_OS_OFFSET window_query(const point_t& p1, const point_t& p2, + stream_t* stream); + + // Find a point. + bool find(const point_t& p); + + // Insert p into the kdbtree. Return true if successful. + bool insert(const point_t& p); + + // Traverse the tree in dfs preorder. Return next node and its level + // (root is on level 0). Start the process by calling this with + // level=-1. + item_t dfs_preorder(int& level); + + // Set persistence. It passes per along to the two collections. + void persist(persistence per); + + // Inquire the (real) parameters. + const AMI_kdbtree_params& params() const { return params_; } + + // Inquire the status. + AMI_kdbtree_status status() const { return status_; }; + + // Inquire the size (number of points stored). + TPIE_OS_OFFSET size() const { return header_.size; } + + // Inquire the mbr_lo point. + point_t mbr_lo() const { return header_.mbr_lo; } + + // Inquire the mbr_hi point. + point_t mbr_hi() const { return header_.mbr_hi; } + + // Inquire the statistics. + const tpie_stats_tree &stats(); + + // Inquire the leaf block size (in bytes) + TPIE_OS_SIZE_T leaf_block_size() const { return pcoll_leaves_->block_size(); } + + // Inquire the node block size (in bytes) + TPIE_OS_SIZE_T node_block_size() const { return pcoll_nodes_->block_size(); } + + // Inquire the base path name. + const string& name() const { return name_; } + + // Destructor. + ~AMI_kdbtree(); + + node_t* fetch_node(AMI_bid bid = 0); + leaf_t* fetch_leaf(AMI_bid bid = 0); + void release_node(node_t* q); + void release_leaf(leaf_t* q); + + class header_t { + public: + unsigned int magic_number; + point_t mbr_lo; + point_t mbr_hi; + AMI_bid root_bid; + TPIE_OS_OFFSET size; + link_type_t root_type; + + header_t(): + magic_number(AMI_KDBTREE_HEADER_MAGIC_NUMBER), mbr_lo(0), mbr_hi(0), + root_bid(0), root_type(BLOCK_LEAF), size(0) {} + }; + + +protected: + + // Function object for the node cache write out. + class remove_node { + public: + void operator()(node_t* p) { delete p; } + }; + // Function object for the leaf cache write out. + class remove_leaf { + public: + void operator()(leaf_t* p) { delete p; } + }; + + // The node cache. + AMI_CACHE_MANAGER* node_cache_; + // The leaf cache. + AMI_CACHE_MANAGER* leaf_cache_; + + // The collection storing the leaves. + collection_t * pcoll_leaves_; + + // The collection storing the internal nodes (could be the same). + collection_t * pcoll_nodes_; + + // Critical information: root bid and type, mbr, size (will be + // stored into the header of the nodes collection). + header_t header_; + + // The status. + AMI_kdbtree_status status_; + + // Run-time parameters. + AMI_kdbtree_params params_; + + // Stack to store the path to a leaf. + stack > path_stack_; + + // Stack for dfs_preorder + stack > dfs_stack_; + + // Statistics object. + tpie_stats_tree stats_; + + // Base path name. + string name_; + + bool insert_empty(const point_t& p); + + TPIE_OS_OFFSET window_query(const item_t& ki, const region_t& r, + stream_t* stream); + + // Various initialization common to all constructors. + void shared_init(const char* base_file_name, AMI_collection_type type); + void kd2kdb(const item_t& ki, b_vector& bv); + void kd2kdb_node( AMI_kdtree_node* bn, + size_t i, region_t r, + b_vector& bv, size_t& bv_pos); + inline TPIE_OS_SIZE_T split_dim_longest_span(const path_stack_item_t& top); + bool split_leaf_and_insert(const path_stack_item_t& top, + leaf_t* bl, item_t& ki1, + item_t& ki2, const point_t& p); + void split_leaf(coord_t sp, TPIE_OS_SIZE_T d, const item_t& kis, + item_t& ki1, item_t& ki2); + void split_node_and_insert(const path_stack_item_t& top, + item_t& ki1, item_t& ki2, const item_t& ki); + void split_node(coord_t sp, TPIE_OS_SIZE_T d, const item_t& kis, + item_t& ki1, item_t& ki2); + void find_split_position(const path_stack_item_t& top, + coord_t& sp, TPIE_OS_SIZE_T& d); + // Empty the path stack. + inline void empty_stack(bool update_weight = false); +}; + +struct _AMI_kdbtree_leaf_info { + TPIE_OS_SIZE_T size; + AMI_bid next; + TPIE_OS_SIZE_T split_dim; +}; + +template +class AMI_kdbtree_leaf: public AMI_block, _AMI_kdbtree_leaf_info, BTECOLL> { +public: + using AMI_block, _AMI_kdbtree_leaf_info, BTECOLL>::info; + using AMI_block, _AMI_kdbtree_leaf_info, BTECOLL>::el; + using AMI_block, _AMI_kdbtree_leaf_info, BTECOLL>::dirty; + + typedef AMI_record point_t; + typedef AMI_record record_t; + typedef AMI_STREAM stream_t; + typedef AMI_collection_single collection_t; + typedef _AMI_kdbtree_leaf_info info_t; + + static TPIE_OS_SIZE_T el_capacity(TPIE_OS_SIZE_T block_size); + + AMI_kdbtree_leaf(collection_t* pcoll, AMI_bid bid = 0): + AMI_block, _AMI_kdbtree_leaf_info, BTECOLL>(pcoll, 0, bid) { + if (bid == 0) { + size() = 0; + next() = 0; + split_dim() = 0; + } + } + + // Number of points stored in this leaf. + TPIE_OS_SIZE_T& size() { return info()->size; } + const TPIE_OS_SIZE_T& size() const { return info()->size; } + + // The weight of a leaf is the size. Just for symmetry with the + // nodes. + const TPIE_OS_OFFSET& weight() const { return info()->size; } + + // Next leaf. All leaves of a tree are chained togther for easy + // retrieval. + const AMI_bid& next() const { return info()->next; } + AMI_bid& next() { return info()->next; } + + TPIE_OS_SIZE_T& split_dim() { return info()->split_dim; } + const TPIE_OS_SIZE_T& split_dim() const { return info()->split_dim; } + + // Maximum number of points that can be stored in this leaf. + TPIE_OS_SIZE_T capacity() const { return el.capacity(); } + + // Find a point. Return the index of the point found in the el + // vector (if not found, return size()). + TPIE_OS_SIZE_T find(const point_t &p) const { + TPIE_OS_SIZE_T i = 0; + while (i < size()) { + if (p == el[i]) + break; + i++; + } + return i; + } + + TPIE_OS_OFFSET window_query(const point_t &lop, const point_t &hip, + stream_t* stream) const { + TPIE_OS_SIZE_T i; + TPIE_OS_OFFSET result = 0; + for (i = 0; i < size(); i++) { + // Test on all dimensions. + if (lop < el[i] && el[i] < hip) { + result++; + if (stream != NULL) + stream->write_item(el[i]); + } + } + return result; + } + + // Insert a point, assuming the leaf is not full. + bool insert(const point_t &p) { + assert(size() < el.capacity()); + if (size() > 0 && find(p) < size()) + return false; + + el[size()] = p; + size()++; + dirty() = 1; + return true; + } + + bool erase(const point_t &p) { + bool ans = false; + TPIE_OS_SIZE_T idx; + if ((idx = find(p)) < size()) { + if (idx < size() - 1) { + // Copy the last item indo pos idx. We could use el.erase() as + // well, but that's slower. Here order is not important. + el[idx] = el[size()-1]; + } + size()--; + ans = true; + dirty() = 1; + } + return ans; + } + + // Sort points on the given dimension. + void sort(TPIE_OS_SIZE_T d) { + typename AMI_record::cmp cmpd(d); + std::sort(&el[0], &el[0] + size(), cmpd); + } + + // Find median point on the given dimension. Return the index of the + // median in the el vector. + TPIE_OS_SIZE_T find_median(TPIE_OS_SIZE_T d) { + sort(d); + TPIE_OS_SIZE_T ans = (size() - 1) / 2; // preliminary median. + /// while ((ans + 1 < size()) && (cmpd.compare(el[ans], el[ans+1]) == 0)) + while ((ans + 1 < size()) && (el[ans][d] == el[ans+1][d])) + ans++; + return ans; + } +}; + + +struct _AMI_kdbtree_node_info { + TPIE_OS_SIZE_T size; + TPIE_OS_OFFSET weight; + TPIE_OS_SIZE_T split_dim; +}; + +// The AMI_kdbtree_node class. +template +class AMI_kdbtree_node: public AMI_block, _AMI_kdbtree_node_info, BTECOLL> { +public: + using AMI_block, _AMI_kdbtree_node_info, BTECOLL>::info; + using AMI_block, _AMI_kdbtree_node_info, BTECOLL>::el; + using AMI_block, _AMI_kdbtree_node_info, BTECOLL>::lk; + using AMI_block, _AMI_kdbtree_node_info, BTECOLL>::dirty; + + typedef AMI_record point_t; + typedef AMI_STREAM stream_t; + typedef AMI_collection_single collection_t; + typedef kdb_item_t item_t; + typedef _AMI_kdbtree_node_info info_t; + + static TPIE_OS_SIZE_T el_capacity(TPIE_OS_SIZE_T block_size); + + // A node is an AMI_block containing kdb_item_t's as elements and no links. + AMI_kdbtree_node(collection_t* pcoll, AMI_bid bid = 0): + AMI_block, _AMI_kdbtree_node_info, BTECOLL>(pcoll, 0, bid) { + if (bid == 0) { + size() = 0; + weight() = 0; + // split_dim() = 0; + } + } + + // Number of kdb_item_t's stored in this node. + TPIE_OS_SIZE_T& size() { return info()->size; } + const TPIE_OS_SIZE_T& size() const { return info()->size; } + + // Weight (ie, number of points stored in the subtree rooted at this + // node). + TPIE_OS_OFFSET& weight() { return info()->weight; } + const TPIE_OS_OFFSET& weight() const { return info()->weight; } + + // Splitting dimension. + TPIE_OS_SIZE_T& split_dim() { return info()->split_dim; } + const TPIE_OS_SIZE_T& split_dim() const { return info()->split_dim; } + + // Maximum number of kdb_item_t's that can be stored in this node. + TPIE_OS_SIZE_T capacity() const { return el.capacity(); } + + // Find the index of the kdb_item_t containing the given point. If + // no item contains the point, return size(). + TPIE_OS_SIZE_T find(const point_t& p) { + TPIE_OS_SIZE_T i; + for (i = 0; i < size(); i++) { + if (el[i].region.contains(p.key)) + break; + } + return i; + } + + // Insert a kdb_item_t in this node. + bool insert(const item_t& ki) { + // assert(size() < el.capacity()); + el[size()] = ki; + size()++; + dirty() = 1; + return true; + } +}; + + +//////////////////////////////////////////////////////////// +/////////////// ***Implementation*** //////////////// +//////////////////////////////////////////////////////////// + +// Define local shortcuts. +#define AMI_KDBTREE AMI_kdbtree +#define AMI_KDBTREE_NODE AMI_kdbtree_node +#define AMI_KDBTREE_LEAF AMI_kdbtree_leaf +#define POINT AMI_record +#define POINT_STREAM AMI_STREAM< POINT > +#define REGION region_t +#define KDB_ITEM kdb_item_t +#define STACK_ITEM path_stack_item_t +#undef TPLOG +#define TPLOG(msg) +// (LOG_APP_DEBUG(msg),LOG_FLUSH_LOG) + +////////////////////////////////////// +////////// **AMI_kdbtree_leaf** ////////// +////////////////////////////////////// + +template +TPIE_OS_SIZE_T AMI_KDBTREE_LEAF::el_capacity(TPIE_OS_SIZE_T block_size) { + return AMI_block, _AMI_kdbtree_leaf_info, BTECOLL>::el_capacity(block_size, 0); +} + +////////////////////////////////////// +////////// **AMI_kdbtree_node** ////////// +////////////////////////////////////// + +template +TPIE_OS_SIZE_T AMI_KDBTREE_NODE::el_capacity(TPIE_OS_SIZE_T block_size) { + return AMI_block::el_capacity(block_size, 0); +} + + +////////////////////////////////////// +///////////// **AMI_kdbtree** //////////// +////////////////////////////////////// + +//// *AMI_kdbtree::AMI_kdbtree* //// +template +AMI_KDBTREE::AMI_kdbtree(const char *base_file_name, AMI_collection_type type, + const AMI_kdbtree_params& params): header_(), params_(params), name_(base_file_name) { + + shared_init(base_file_name, type); +} + +//// *AMI_kdbtree::AMI_kdbtree* //// +template +AMI_KDBTREE::AMI_kdbtree(const string &base_file_name, AMI_collection_type type, + const AMI_kdbtree_params& params): header_(), params_(params), name_(base_file_name) { + + shared_init(base_file_name.c_str(), type); +} + +//// *AMI_kdbtree::shared_init* //// +template +void AMI_KDBTREE::shared_init(const char* base_file_name, AMI_collection_type type) { + + assert(base_file_name != NULL); + char collname[124]; + + // Open the two block collections. + strncpy(collname, base_file_name, 124 - 2); + strcat(collname, ".l"); + pcoll_leaves_ = new collection_t(collname, type, params_.leaf_block_factor); + + strncpy(collname, base_file_name, 124 - 2); + strcat(collname, ".n"); + pcoll_nodes_ = new collection_t(collname, type, params_.node_block_factor); + + if (pcoll_nodes_->status() != AMI_COLLECTION_STATUS_VALID || + pcoll_leaves_->status() != AMI_COLLECTION_STATUS_VALID) { + status_ = AMI_KDBTREE_STATUS_INVALID; + delete pcoll_leaves_; + delete pcoll_nodes_; + return; + } + + // Read the header info, if relevant. + if (pcoll_leaves_->size() != 0) { + unsigned int magic = *((unsigned int *) pcoll_nodes_->user_data()); + if (magic == AMI_KDTREE_HEADER_MAGIC_NUMBER) { + status_ = AMI_KDBTREE_STATUS_KDTREE; + } else if (magic == AMI_KDBTREE_HEADER_MAGIC_NUMBER) { + status_ = AMI_KDBTREE_STATUS_VALID; + // header_ = *((header_t *) pcoll_nodes_->user_data()); + memcpy((void *)(&header_), pcoll_nodes_->user_data(), sizeof(header_)); + // TODO: sanity checks on the header. + } else { + status_ = AMI_KDBTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("Invalid kdbtree magic number:"<(params_.leaf_cache_size, 4); + node_cache_ = new AMI_CACHE_MANAGER(params_.node_cache_size, 1); + + // Give meaningful values to parameters, if necessary. + TPIE_OS_SIZE_T leaf_capacity = AMI_KDBTREE_LEAF::el_capacity(pcoll_leaves_->block_size()); + if (params_.leaf_size_max == 0 || params_.leaf_size_max > leaf_capacity) + params_.leaf_size_max = leaf_capacity; + TPLOG(" AMI_kdbtree::shared_init leaf_size_max="<block_size()); + if (params_.node_size_max == 0 || params_.node_size_max > node_capacity) + params_.node_size_max = node_capacity; + TPLOG(" AMI_kdbtree::shared_init node_size_max="<block_factor(); + params_.node_block_factor = pcoll_nodes_->block_factor(); +} + + +//// *AMI_kdbtree::kd2kdb* //// +template +bool AMI_KDBTREE::kd2kdb() { + TPLOG("AMI_kdbtree::kd2kdb() Entering " << "\n"); + if (status_ != AMI_KDBTREE_STATUS_KDTREE) { + TP_LOG_WARNING_ID(" kd2kdb: status is not AMI_KDBTREE_STATUS_KDTREE. operation aborted."); + return false; + } + + bool ans = true; + typename AMI_kdtree::header_t kdheader; + memcpy((void *)(&kdheader), pcoll_nodes_->user_data(), sizeof(kdheader)); + header_.root_bid = kdheader.root_bid; + header_.root_type = kdheader.root_type; + header_.size = kdheader.size; + header_.mbr_lo = kdheader.mbr_lo; + header_.mbr_hi = kdheader.mbr_hi; + + REGION r; // Unbounded region, corresponding to the root. + KDB_ITEM ki(r, header_.root_bid, header_.root_type); + if (ki.type == BLOCK_NODE) { + + // Create temporary buffer. + AMI_KDBTREE_NODE* buffer = new AMI_KDBTREE_NODE(pcoll_nodes_); + // Do the job. + kd2kdb(ki, buffer->el); + // Dispose of the temporary buffer. + buffer->persist(PERSIST_DELETE); + delete buffer; + + } else { + // Just a leaf. Nothing to do. + } + + if (status_ == AMI_KDBTREE_STATUS_KDTREE) + status_ = AMI_KDBTREE_STATUS_VALID; + else + ans = false; + + TPLOG("AMI_kdbtree::kd2kdb() Exiting ans=" << ans << "\n"); + return ans; +} + + +//// *AMI_kdbtree::window_query* //// +template +TPIE_OS_OFFSET AMI_KDBTREE::window_query(const POINT &p1, const POINT& p2, + POINT_STREAM* stream) { + TPLOG(" query window: "< +TPIE_OS_OFFSET AMI_KDBTREE::window_query(const KDB_ITEM& ki, const REGION& r, POINT_STREAM* stream) { + TPLOG(" window query recusion: " << ki << "\n"); + TPIE_OS_OFFSET result = 0; + if (ki.type == BLOCK_NODE) { + AMI_KDBTREE_NODE* bn = fetch_node(ki.bid); + TPIE_OS_SIZE_T i; + for (i = 0; i < bn->size(); i++) { + if (bn->el[i].region.intersects(r)) + result += window_query(bn->el[i], r, stream); + } + release_node(bn); + } else { + assert(ki.type == BLOCK_LEAF); + AMI_KDBTREE_LEAF* bl = fetch_leaf(ki.bid); + result += bl->window_query(r.point_lo(), r.point_hi(), stream); + release_leaf(bl); + } + return result; +} + +//// *AMI_kdbtree::kd2kdb* //// +template +void AMI_KDBTREE::kd2kdb(const KDB_ITEM& ki, b_vector& bv) { + TPLOG("AMI_kdbtree::kd2kdb Entering bid=" << ki.bid << "\n"); + + AMI_kdtree_node *bno; + AMI_KDBTREE_NODE *bn; + // link_type_t ni_type; + bno = new AMI_kdtree_node(pcoll_nodes_, ki.bid); + if (bno->size() + 1 > params_.node_size_max) { + TP_LOG_FATAL_ID(" kd2kdb: wrong kdtree node size;"); + TP_LOG_FATAL_ID(" kd2kdb: kdbtree node capacity: " << static_cast(params_.node_size_max)); + TP_LOG_FATAL_ID(" kd2kdb: max kdtree node size allowed: " << static_cast(params_.node_size_max-1)); + TP_LOG_FATAL_ID(" kd2kdb: found kdtree node with size: " << static_cast(bno->size())); + TP_LOG_FATAL_ID(" kd2kdb: operation aborted."); + delete bno; + status_ = AMI_KDBTREE_STATUS_INVALID; + } else { + TPIE_OS_SIZE_T free_pos = 0; + kd2kdb_node(bno, 0, ki.region, bv, free_pos); + delete bno; + TPLOG(" AMI_kdbtree_node size="<el.copy(0, free_pos, bv, 0); + bn->size() = free_pos; + bn->split_dim() = 0; + + for (TPIE_OS_SIZE_T i = 0; i < free_pos; i++) { + if (bn->el[i].type == BLOCK_NODE && status_ != AMI_KDBTREE_STATUS_INVALID) + kd2kdb(bn->el[i], bv); + } + delete bn; + } + + TPLOG("AMI_kdbtree::kd2kdb Exiting bid=" << ki.bid << "\n"); +} + +//// *AMI_kdbtree::kd2kdb_node* //// +template +void AMI_KDBTREE::kd2kdb_node(AMI_kdtree_node *bn, + size_t i, REGION r, + b_vector& bv, size_t& bv_pos) { + TPLOG("AMI_kdbtree::kd2kdb_node Entering " << "\n"); + + REGION ni_r; + size_t ni; + link_type_t ni_type; + + ni_r = r; + bn->el[i].get_low_child(ni, ni_type); + ni_r.cutout_hi(bn->el[i].get_discriminator_val(), bn->el[i].get_discriminator_dim()); + if (ni_type == BIN_NODE) { + kd2kdb_node(bn, ni, ni_r, bv, bv_pos); + } else { + assert(ni_type == BLOCK_LEAF || ni_type == BLOCK_NODE); + bv[bv_pos].region = ni_r; + bv[bv_pos].bid = bn->lk[ni]; + bv[bv_pos].type = ni_type; + bv_pos++; + } + + ni_r = r; + bn->el[i].get_high_child(ni, ni_type); + ni_r.cutout_lo(bn->el[i].get_discriminator_val(), bn->el[i].get_discriminator_dim()); + if (ni_type == BIN_NODE) { + kd2kdb_node(bn, ni, ni_r, bv, bv_pos); + } else { + assert(ni_type == BLOCK_LEAF || ni_type == BLOCK_NODE); + bv[bv_pos].region = ni_r; + bv[bv_pos].bid = bn->lk[ni]; + bv[bv_pos].type = ni_type; + bv_pos++; + } + TPLOG("AMI_kdbtree::kd2kdb_node Exiting " << "\n"); +} + +//// *AMI_kdbtree::insert_empty* //// +template +bool AMI_KDBTREE::insert_empty(const AMI_record& p) { + bool ans; + AMI_KDBTREE_LEAF* bl = fetch_leaf(); + ans = bl->insert(p); + assert(ans); + bl->split_dim() = 0; + bl->next() = 0; + + header_.size = 1; + header_.root_bid = bl->bid(); + header_.root_type = BLOCK_LEAF; + header_.mbr_lo = p; + header_.mbr_lo.id() = 1; + header_.mbr_hi = p; + header_.mbr_hi.id() = 1; + + status_ = AMI_KDBTREE_STATUS_VALID; + release_leaf(bl); + return ans; +} + +//// *AMI_kdbtree::find* //// +template +bool AMI_KDBTREE::find(const AMI_record& p) { + + TPLOG("AMI_kdbtree::find Entering "<<"\n"); + + if (header_.size == 0) + return false; + + bool ans; + TPIE_OS_SIZE_T i; + + AMI_KDBTREE_NODE* bn; + REGION r; + KDB_ITEM ki(r, header_.root_bid, header_.root_type); + STACK_ITEM si(ki, 0); + + while (si.item.type == BLOCK_NODE) { + bn = fetch_node(si.item.bid); + + i = bn->find(p); + assert(i < bn->size()); + si.item = bn->el[i]; + // si.el_idx = i; + release_node(bn); + } + + assert(si.item.type == BLOCK_LEAF); + + // Fetch the leaf. + AMI_KDBTREE_LEAF* bl = fetch_leaf(si.item.bid); + // Check whether item is in the leaf. + ans = (bl->find(p) < bl->size()); + // Release the leaf. + release_leaf(bl); + + TPLOG("AMI_kdbtree::find Exiting ans="< +bool AMI_KDBTREE::insert(const AMI_record& p) { + + TPLOG("AMI_kdbtree::insert Entering "<<"\n"); + TPIE_OS_SIZE_T i; + + // The first insertion is treated separately. + if (header_.size == 0) + return insert_empty(p); + + // Update the MBR. + for (i = 0; i < dim; i++) { + header_.mbr_lo[i] = min(header_.mbr_lo[i], p[i]); + header_.mbr_hi[i] = max(header_.mbr_hi[i], p[i]); + } + + bool ans; + AMI_KDBTREE_NODE* bn; + REGION r; // Infinite region. + KDB_ITEM ki(r, header_.root_bid, header_.root_type); + + // Stack item; initially unbounded, corresponding to the root node. + STACK_ITEM si(ki, 0); + path_stack_.push(si); + + // Go down the tree until the appropriate leaf is found. + while (si.item.type == BLOCK_NODE) { + bn = fetch_node(si.item.bid); + + i = bn->find(p); + assert(i < bn->size()); + si.item = bn->el[i]; + si.d = (si.d + 1) % dim; + si.el_idx = i; + + release_node(bn); + path_stack_.push(si); + } + + // Make sure we reached a leaf. + assert(si.item.type == BLOCK_LEAF); + + // Fetch the leaf. + AMI_KDBTREE_LEAF* bl = fetch_leaf(si.item.bid); + + // Check for duplicate key. For now, just exit with false answer if found. + if (bl->find(p) < bl->size()) { // Found. + + ans = false; + release_leaf(bl); + empty_stack(ans); + + } else if (bl->size() < params_.leaf_size_max) { + // The very easy case. Just insert into the leaf. + + ans = bl->insert(p); + release_leaf(bl); + empty_stack(ans); + + } else { + // Need to split the leaf. Maybe some nodes, too. + + KDB_ITEM ki1, ki2; + STACK_ITEM top; + + // Pop the leaf from the stack. + top = path_stack_.top(); + path_stack_.pop(); + + // Split bl into two leaves, one of which is bl, insert p into the + // appropriate leaf, store the kdb_item_t's pointing to the two + // leaves in ki1 and ki2, and return true if insertion was + // successful. + ans = split_leaf_and_insert(top, bl, ki1, ki2, p); + + release_leaf(bl); + + bool done = false; + TPIE_OS_SIZE_T el_idx; + + while (!path_stack_.empty() && !done) { + // Save top.el_idx. + el_idx = top.el_idx; + + // Pop the next node on the path to the root. + top = path_stack_.top(); + path_stack_.pop(); + + // Fetch the node. + bn = fetch_node(top.item.bid); + // Update bn. + bn->el[el_idx] = ki1; + if (ans) bn->weight()++; + + if (bn->size() == params_.node_size_max) { + + // Split bn into two nodes, one of which is bn, insert ki2 + // into the appropriate node, store the resulting regions into + // ki1 and ki2 (for next iteration). + ki = ki2; + release_node(bn); + split_node_and_insert(top, ki1, ki2, ki); + + } else { + // Insert ki2 into bn and exit the while loop. + bn->insert(ki2); + done = true; + release_node(bn); + } + } // end of while. + + // Check if root was split. + if (path_stack_.empty() && !done) { + // Create new root node. + bn = fetch_node(); + // Insert the two kdb_item's into this new root node. + bn->insert(ki1); + bn->insert(ki2); + // Update the header information. + header_.root_bid = bn->bid(); + header_.root_type = BLOCK_NODE; + bn->split_dim() = 0; + release_node(bn); + } else { + empty_stack(ans); + } + } + + if (ans) header_.size++; + + TPLOG("AMI_kdbtree::insert Exiting ans="< +KDB_ITEM AMI_KDBTREE::dfs_preorder(int& level) { + + // level signals the start/end of the traversal. All necessary state + // information is kept on dfs_stack_. + + if (level == -1) { + + // Empty the stack. This allows restarts in the middle of a + // traversal. All previous state information is lost. + while (!dfs_stack_.empty()) + dfs_stack_.pop(); + REGION r; // Infinite region. + KDB_ITEM ki(r, header_.root_bid, header_.root_type); + // Push the root region on the stack. + dfs_stack_.push(STACK_ITEM(ki, 0, 0)); + + level = (int)dfs_stack_.size() - 1; + return ki; + + } else { + + AMI_KDBTREE_NODE* bn; + KDB_ITEM ki; + if (dfs_stack_.top().item.type == BLOCK_NODE) { + // Fetch the node ... + bn = fetch_node(dfs_stack_.top().item.bid); + // ... and get the appropriate child. + ki = bn->el[dfs_stack_.top().el_idx]; + dfs_stack_.push(STACK_ITEM(ki, 0, 0)); + release_node(bn); + } else { // i.e., dfs_stack_.top().item.type == BLOCK_LEAF + // Remove the leaf from the stack. + dfs_stack_.pop(); + bool done = false; + while (!dfs_stack_.empty() && !done) { + // Fetch the node ... + bn = fetch_node(dfs_stack_.top().item.bid); + + (dfs_stack_.top().el_idx)++; + if (dfs_stack_.top().el_idx < bn->size()) { + ki = bn->el[dfs_stack_.top().el_idx]; + dfs_stack_.push(STACK_ITEM(ki, 0, 0)); + done = true; + } else + dfs_stack_.pop(); + + release_node(bn); + } + } + + // Note: if stack is empty, level will be -1, signaling the end of + // the traversal. + level = (int)dfs_stack_.size() - 1; + return ki; + } +} + +//// *AMI_kdbtree::split_leaf_and_insert* //// +template +TPIE_OS_SIZE_T AMI_KDBTREE::split_dim_longest_span(const STACK_ITEM& top) { + TPIE_OS_SIZE_T d; + + coord_t longest_span = + (top.item.region.is_bounded_hi(0) ? top.item.region.hi(0): header_.mbr_hi[0]) - + (top.item.region.is_bounded_lo(0) ? top.item.region.lo(0): header_.mbr_lo[0]); + d = 0; + for (TPIE_OS_SIZE_T i = 1; i < dim; i++) + if ((top.item.region.is_bounded_hi(i) ? top.item.region.hi(i): header_.mbr_hi[i]) - + (top.item.region.is_bounded_lo(i) ? top.item.region.lo(i): header_.mbr_lo[i]) > longest_span) { + longest_span = + (top.item.region.is_bounded_hi(i) ? top.item.region.hi(i): header_.mbr_hi[i]) - + (top.item.region.is_bounded_lo(i) ? top.item.region.lo(i): header_.mbr_lo[i]); + d = i; + } + + return d; +} + +//// *AMI_kdbtree::split_leaf_and_insert* //// +template +bool AMI_KDBTREE::split_leaf_and_insert(const STACK_ITEM& top, AMI_KDBTREE_LEAF* bl, + KDB_ITEM& ki1, KDB_ITEM& ki2, const POINT& p) { + // Get the median point. + // Move points higher than the median in bl_hi. + AMI_KDBTREE_LEAF* bl_hi = fetch_leaf(); + bool ans; + TPIE_OS_SIZE_T d; + if (params_.split_heuristic == CYCLICAL) + d = bl->split_dim(); + else if (params_.split_heuristic == LONGEST_SPAN) + d = split_dim_longest_span(top); + else if (params_.split_heuristic == RANDOM) + d = TPIE_OS_RANDOM() % dim; + TPIE_OS_SIZE_T med = bl->find_median(d); // the index of the median point in bl->el. + POINT sp = bl->el[med]; + + if (med + 1 >= bl->size()) { + cerr << "\nbl->bid()=" << bl->bid() << ", bl->size()=" << static_cast(bl->size()) + << ", med=" << static_cast(med) << "\n"; + cerr << "bl: "; + for (TPIE_OS_SIZE_T i = 0; i < bl->size(); i++) { + cerr << "[" << bl->el[i][0] << "," << bl->el[i][1] << "] "; + } + cerr << "\n"; + } + assert(med + 1 < bl->size()); + bl_hi->size() = bl->size() - (med + 1); // the size of bl_hi + bl->size() = med + 1; // the new size of bl + + // Cycle through dimensions. + bl->split_dim() = (d + 1) % dim; + bl_hi->split_dim() = (d + 1) % dim; + // Update next pointers. + bl_hi->next() = bl->next(); + bl->next() = bl_hi->bid(); + + bl_hi->el.copy(0, bl_hi->size(), bl->el, med + 1); // copy points from bl to bl_hi. + + assert(top.item.type == BLOCK_LEAF); + ki1 = top.item; + ki1.region.cutout_hi(sp[d], d); + assert(ki1.bid == bl->bid()); + + ki2 = top.item; + ki2.region.cutout_lo(sp[d], d); + ki2.bid = bl_hi->bid(); + + ans = (ki1.region.contains(p.key) ? bl: bl_hi)->insert(p); + release_leaf(bl_hi); + return ans; +} + +//// *AMI_kdbtree::split_leaf* //// +template +void AMI_KDBTREE::split_leaf(coord_t sp, TPIE_OS_SIZE_T d, const KDB_ITEM& kis, + KDB_ITEM& ki1, KDB_ITEM& ki2) { + + assert(kis.type == BLOCK_LEAF); + AMI_KDBTREE_LEAF* bl = fetch_leaf(kis.bid); + AMI_KDBTREE_LEAF* bl_hi = fetch_leaf(); + TPIE_OS_SIZE_T bl_size = bl->size(); + POINT p; + + bl->size() = 0; + assert(bl_hi->size() == 0); + for (TPIE_OS_SIZE_T i = 0; i < bl_size; i++) { + p = bl->el[i]; + (sp < p[d] ? bl_hi: bl)->insert(p); + } + + ki1 = kis; + ki1.region.cutout_hi(sp, d); + assert(ki1.bid == bl->bid()); + + ki2 = kis; + ki2.region.cutout_lo(sp, d); + ki2.bid = bl_hi->bid(); + + release_leaf(bl_hi); + release_leaf(bl); +} + +//// *AMI_kdbtree::split_node_and_insert* //// +template +void AMI_KDBTREE::split_node_and_insert(const STACK_ITEM& top, + KDB_ITEM& ki1, KDB_ITEM& ki2, const KDB_ITEM& ki) { + // The split position. + coord_t sp; + // The split dimension. + TPIE_OS_SIZE_T d; + + // Find a split position and store it in sp. + find_split_position(top, sp, d); + + // Split. + split_node(sp, d, top.item, ki1, ki2); + + // Insert ki. + AMI_KDBTREE_NODE *bn, *bn_hi; + bn = fetch_node(ki1.bid); + bn_hi = fetch_node(ki2.bid); + int pos = ki.region.relative_to_plane(sp, d); + if (pos == -1) { + assert(bn->size() < params_.node_size_max); + bn->insert(ki); + } else if (pos == 1) { + assert(bn_hi->size() < params_.node_size_max); + bn_hi->insert(ki); + } else { + assert(bn->size() < params_.node_size_max && bn_hi->size() < params_.node_size_max); + KDB_ITEM lki1, lki2; + + if (ki.type == BLOCK_LEAF) + split_leaf(sp, d, ki, lki1, lki2); + else + split_node(sp, d, ki, lki1, lki2); + + bn->insert(lki1); + bn_hi->insert(lki2); + } + + bn->split_dim() = (d + 1) % dim; + bn_hi->split_dim() = (d + 1) % dim; + release_node(bn_hi); + release_node(bn); +} + +//// *AMI_kdbtree::find_split_position* //// +template +void AMI_KDBTREE::find_split_position(const STACK_ITEM& top, coord_t& sp, TPIE_OS_SIZE_T& d) { + AMI_KDBTREE_NODE *bn; + bn = fetch_node(top.item.bid); + + // Find split dimension. +#if 0 + if (params_.split_heuristic == CYCLICAL) + d = bn->split_dim(); + else if (params_.split_heuristic == LONGEST_SPAN) + d = split_dim_longest_span(top); + else if (params_.split_heuristic == RANDOM) + d = TPIE_OS_RANDOM() % dim; +#else + d = bn->split_dim(); +#endif + + vector cv(0); + TPIE_OS_SIZE_T unbounded = 0; + // Collect all low boundaries from bn and, if they are bounded, + // store them in cv. The unbounded ones are counted only. + for (TPIE_OS_SIZE_T i = 0; i < bn->size(); i++) { + if (bn->el[i].region.is_bounded_lo(d)) + cv.push_back(bn->el[i].region.lo(d)); + else + unbounded++; + } + assert(cv.size() > 0); + // Sort. + std::sort(cv.begin(), cv.end()); + // Get median value. + TPIE_OS_SIZE_T median = (bn->size() / 2 > unbounded ? bn->size() / 2 - unbounded: 0); + // Make sure we don't return the leftmost boundary. + if (unbounded == 0) + while (cv[median] == cv[0]) + median++; + assert(median < cv.size()); + sp = cv[median]; + release_node(bn); +} + +//// *AMI_kdbtree::split_node* //// +template +void AMI_KDBTREE::split_node(coord_t sp, TPIE_OS_SIZE_T d, const KDB_ITEM& kis, + KDB_ITEM& ki1, KDB_ITEM& ki2) { + assert(kis.type == BLOCK_NODE); + AMI_KDBTREE_NODE *bn, *bn_hi; + + bn = fetch_node(kis.bid); + bn_hi = fetch_node(); + KDB_ITEM ki; + /// TPIE_OS_SIZE_T d = bn->split_dim(); + + ki1 = ki2 = kis; + ki1.region.cutout_hi(sp, d); + assert(ki1.bid == bn->bid()); + ki2.region.cutout_lo(sp, d); + ki2.bid = bn_hi->bid(); + + TPIE_OS_SIZE_T next_free_lo = 0, next_free_hi = 0, i; + TPIE_OS_SIZE_T bn_size = bn->size(); + int pos; + bn->size() = 0; + assert(bn_hi->size() == 0); + // Cycle through dimensions. + /// bn->split_dim() = bn_hi->split_dim() = (d + 1) % dim; + + for (i = 0; i < bn_size; i++) { + pos = bn->el[i].region.relative_to_plane(sp, d); + ki = bn->el[i]; + if (pos == -1) { + // Move to bn. + bn->insert(ki); + } else if (pos == 1) { + // Move to bn_hi; + bn_hi->insert(ki); + } else { + // The hard case: intersection. + if (ki.type == BLOCK_LEAF) + split_leaf(sp, d, ki, bn->el[bn->size()], bn_hi->el[bn_hi->size()]); + else // BLOCK_NODE + split_node(sp, d, ki, bn->el[bn->size()], bn_hi->el[bn_hi->size()]); + bn->size()++; + bn_hi->size()++; + } + } + + release_node(bn_hi); + release_node(bn); +} + +//// *AMI_kdbtree::empty_stack* //// +template +void AMI_KDBTREE::empty_stack(bool update_weight) { + node_t* bn; + while (!path_stack_.empty()) { + if (update_weight && path_stack_.top().item.type == BLOCK_NODE) { + bn = fetch_node(path_stack_.top().item.bid); + bn->weight()++; + release_node(bn); + } + path_stack_.pop(); + } +} + +//// *AMI_kdbtree::persist* //// +template +void AMI_KDBTREE::persist(persistence per) { + pcoll_leaves_->persist(per); + pcoll_nodes_->persist(per); +} + +//// *AMI_kdbtree::fetch_node* //// +template +AMI_KDBTREE_NODE* AMI_KDBTREE::fetch_node(AMI_bid bid) { + AMI_KDBTREE_NODE* q; + stats_.record(NODE_FETCH); + // Warning: using short-circuit evaluation. Order is important. + if ((bid == 0) || !node_cache_->read(bid, q)) { + q = new AMI_KDBTREE_NODE(pcoll_nodes_, bid); + } + return q; +} + +//// *AMI_kdbtree::fetch_leaf* //// +template +AMI_KDBTREE_LEAF* AMI_KDBTREE::fetch_leaf(AMI_bid bid) { + AMI_KDBTREE_LEAF* q; + stats_.record(LEAF_FETCH); + // Warning: using short-circuit evaluation. Order is important. + if ((bid == 0) || !leaf_cache_->read(bid, q)) { + q = new AMI_KDBTREE_LEAF(pcoll_leaves_, bid); + } + return q; +} + +//// *AMI_kdbtree::release_node* //// +template +void AMI_KDBTREE::release_node(AMI_KDBTREE_NODE* q) { + stats_.record(NODE_RELEASE); + if (q->persist() == PERSIST_DELETE) + delete q; + else + node_cache_->write(q->bid(), q); +} + +//// *AMI_kdbtree::release_leaf* //// +template +void AMI_KDBTREE::release_leaf(AMI_KDBTREE_LEAF* q) { + stats_.record(LEAF_RELEASE); + if (q->persist() == PERSIST_DELETE) + delete q; + else + leaf_cache_->write(q->bid(), q); +} + +//// *AMI_kdbtree::stats* //// +template +const tpie_stats_tree &AMI_KDBTREE::stats() { + node_cache_->flush(); + leaf_cache_->flush(); + stats_.set(LEAF_READ, pcoll_leaves_->stats().get(BLOCK_GET)); + stats_.set(LEAF_WRITE, pcoll_leaves_->stats().get(BLOCK_PUT)); + stats_.set(LEAF_CREATE, pcoll_leaves_->stats().get(BLOCK_NEW)); + stats_.set(LEAF_DELETE, pcoll_leaves_->stats().get(BLOCK_DELETE)); + stats_.set(LEAF_COUNT, pcoll_leaves_->size()); + stats_.set(NODE_READ, pcoll_nodes_->stats().get(BLOCK_GET)); + stats_.set(NODE_WRITE, pcoll_nodes_->stats().get(BLOCK_PUT)); + stats_.set(NODE_CREATE, pcoll_nodes_->stats().get(BLOCK_NEW)); + stats_.set(NODE_DELETE, pcoll_nodes_->stats().get(BLOCK_DELETE)); + stats_.set(NODE_COUNT, pcoll_nodes_->size()); + return stats_; +} + + +//// *AMI_kdbtree::~AMI_kdbtree* //// +template +AMI_KDBTREE::~AMI_kdbtree() { + + if (status_ == AMI_KDBTREE_STATUS_VALID) { + // Write initialization info into the pcoll_nodes_ header. + // *((header_t *) pcoll_nodes_->user_data()) = header_; + memcpy(pcoll_nodes_->user_data(), (void *)(&header_), sizeof(header_)); + } + + delete node_cache_; + delete leaf_cache_; + + // Delete the two collections. + delete pcoll_leaves_; + delete pcoll_nodes_; + +} + +// Undefine shortcuts. +#undef AMI_KDBTREE +#undef AMI_KDBTREE_NODE +#undef AMI_KDBTREE_LEAF +#undef POINT +#undef POINT_STREAM +#undef REGION +#undef KDB_ITEM +#undef STACK_ITEM + +#endif // _AMI_KDBTREE_H diff --git a/fastlib/u/nvasil/tpie/ami_kdtree.h b/fastlib/u/nvasil/tpie/ami_kdtree.h new file mode 100644 index 0000000000..8804d350e2 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_kdtree.h @@ -0,0 +1,3471 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: ami_kdtree.h +// Author: Octavian Procopiuc +// +// Blocked kd-tree definition and implementation. +// +// $Id: ami_kdtree.h,v 1.19 2005/02/12 20:27:36 tavi Exp $ +// + +#ifndef _AMI_KDTREE_H +#define _AMI_KDTREE_H + +// Get definitions for working with Unix and Windows +#include +// For pair. +#include +// For stack. +#include +// For min, max. +#include +// For vector. +#include +// For priority_queue. +#include +// STL string. +#include + +// TPIE stuff. +#include +#include +#include +#include +#include +// The tpie_stats_tree class. +#include +// The cache manager. +#include +// The AMI_point/AMI_record classes. +#include +// Supporting types: AMI_kdtree_status, AMI_kdtree_params, etc. +#include + +// Forward references. +template class AMI_kdtree_leaf; +template class AMI_kdtree_node; + +// A global object storing the default parameter values. +const AMI_kdtree_params _AMI_kdtree_params_default = AMI_kdtree_params(); + +#define AMI_KDTREE_HEADER_MAGIC_NUMBER 0xA9420E + +#define TPLOG(msg) +// (LOG_APP_DEBUG(msg),LOG_FLUSH_LOG) + + +// The AMI_kdtree class. +template, class BTECOLL = BTE_COLLECTION > +class AMI_kdtree { +public: + + typedef AMI_record point_t; + typedef AMI_record record_t; + typedef AMI_point key_t; + typedef AMI_STREAM stream_t; + typedef AMI_collection_single collection_t; + typedef AMI_kdtree_node node_t; + typedef AMI_kdtree_leaf leaf_t; + + // Constructor. + AMI_kdtree(const AMI_kdtree_params& params = _AMI_kdtree_params_default); + + // Constructor. Open/create a new kdtree with the given name, type + // and parameters. + AMI_kdtree(const char* base_file_name, + AMI_collection_type type = AMI_WRITE_COLLECTION, + const AMI_kdtree_params& params = _AMI_kdtree_params_default); + + AMI_kdtree(const string& base_file_name, + AMI_collection_type type = AMI_WRITE_COLLECTION, + const AMI_kdtree_params& params = _AMI_kdtree_params_default); + + // Sort in_stream on each of the dim coordinates and store the + // sorted streams in the given array. If out_streams[i] is NULL, a + // new temporary stream is created. + AMI_err sort(stream_t* in_stream, stream_t* out_streams[]); + + // Bulk load a kd-tree from the stream of points set during + // construction. + AMI_err load_sorted(stream_t* streams_s[], + float lfill = 0.75, float nfill = 0.75, + int load_method = AMI_KDTREE_LOAD_SORT | AMI_KDTREE_LOAD_GRID); + + // A shortcut for sort+load_sorted. + AMI_err load(stream_t* in_stream, + float lfill = 0.75, float nfill = 0.75, + int load_method = AMI_KDTREE_LOAD_SORT | AMI_KDTREE_LOAD_GRID); + + // Bulk load using sampling, thus avoiding the sorting step. + AMI_err load_sample(stream_t* in_stream); + + // Write all points stored in the tree to the given stream. No + // changes are made to the tree. + AMI_err unload(stream_t* s); + + // Report the k nearest neighbors of point p. + TPIE_OS_OFFSET k_nn_query(const point_t &p, stream_t* stream, TPIE_OS_OFFSET k); + + // Report all points inside the window determined by p1 and p2. If + // stream is NULL, only *count* the points inside the window. NB: + // Counting is much faster than reporting, because (a) no points are + // written out, and (b) the weights of nodes and leaves are used to + // speed up the search. + TPIE_OS_OFFSET window_query(const point_t& p1, const point_t& p2, stream_t* stream); + + // Find a point. Return true if found, false otherwise. + bool find(const point_t& p); + + // (Try to) insert a point. Return true if successful. + bool insert(const point_t& p); + + // Delete a point. Return true if found and deleted. No tree + // reorganizaton is performed. Leaves with no points are kept in the + // tree (to insure correctness of leaves' threading). + bool erase(const point_t& p); + + // Return the number of points stored in the tree. + TPIE_OS_OFFSET size() const { return header_.size; } + + // Set the persistence. It passes per along to the two block + // collections. + void persist(persistence per); + + // Inquire the (real) parameters. + const AMI_kdtree_params& params() const { return params_; } + + // Inquire the status. + AMI_kdtree_status status() const { return status_; }; + + // Inquire the mbr_lo point. + point_t mbr_lo() const { return header_.mbr_lo; } + + // Inquire the mbr_hi point. + point_t mbr_hi() const { return header_.mbr_hi; } + + // Inquire the statistics. + const tpie_stats_tree &stats(); + + // Print out some stuff about the tree structure. For debugging + // purposes only. + void print(ostream& s); + // Print the BINARY kd-tree indented. + void print(ostream& s, bool print_mbr, bool print_level, char indent_char = ' '); + + // Inquire the base path name. + const string& name() const { return name_; } + + // Destructor. + ~AMI_kdtree(); + + // Inquire the number of Bin_node's. + TPIE_OS_OFFSET bin_node_count() const { return bin_node_count_; } + + class header_t { + public: + unsigned int magic_number; + point_t mbr_lo; + point_t mbr_hi; + AMI_bid root_bid; + TPIE_OS_OFFSET size; + link_type_t root_type; + unsigned char store_weights; + unsigned char use_exact_split; + unsigned char use_kdbtree_leaf; + unsigned char use_real_median; + + header_t(): + magic_number(AMI_KDTREE_HEADER_MAGIC_NUMBER), mbr_lo(0), mbr_hi(0), + root_bid(0), root_type(BLOCK_LEAF), size(0), + store_weights(AMI_KDTREE_STORE_WEIGHTS), + use_exact_split(AMI_KDTREE_USE_EXACT_SPLIT), + use_kdbtree_leaf(AMI_KDTREE_USE_KDBTREE_LEAF), + use_real_median(AMI_KDTREE_USE_REAL_MEDIAN) {} + }; + + +protected: + + // Function object for the node cache write out. + class remove_node { + public: + void operator()(node_t* p) { delete p; } + }; + // Function object for the leaf cache write out. + class remove_leaf { + public: + void operator()(leaf_t* p) { delete p; } + }; + + typedef AMI_CACHE_MANAGER node_cache_t; + typedef AMI_CACHE_MANAGER leaf_cache_t; + + // The node cache. + node_cache_t* node_cache_; + // The leaf cache. + leaf_cache_t* leaf_cache_; + + // The collection storing the leaves. + collection_t* pcoll_leaves_; + + // The collection storing the internal nodes (could be the same). + collection_t* pcoll_nodes_; + + // Critical information: root bid and type, mbr, size (will be + // stored into the header of the nodes collection). + header_t header_; + + // This points to the first leaf in the order given by the next + // pointers stored in leaves. Used by unload to start the leaf + // traversal. + AMI_bid first_leaf_id_; + + leaf_t* previous_leaf_; + + // The status. + AMI_kdtree_status status_; + + // Run-time parameters. + AMI_kdtree_params params_; + + // One comparison object for each dimension. + typename point_t::cmp* comp_obj_[dim]; + + // Statistics object. + tpie_stats_tree stats_; + + // The total number of bin nodes. + TPIE_OS_OFFSET bin_node_count_; + + // Base path name. + string name_; + + // Various initialization common to all constructors. + void shared_init(const char* base_file_name, AMI_collection_type type); + + TPIE_OS_OFFSET real_median(TPIE_OS_OFFSET sz) { return (sz - 1) / 2; } + TPIE_OS_SIZE_T real_median(TPIE_OS_SIZE_T sz) { return (sz - 1) / 2; } + + // Return the position of the median point. + TPIE_OS_OFFSET median(TPIE_OS_OFFSET sz) { +#if AMI_KDTREE_USE_REAL_MEDIAN + return real_median(sz); +#else + int i = 0; + while ((1 << i) < ((sz + params_.leaf_size_max - 1) / + params_.leaf_size_max)) + i++; + return (1 << (i-1)) * params_.leaf_size_max - 1; +#endif + } + + // Return the position of the median point. + TPIE_OS_SIZE_T median(TPIE_OS_SIZE_T sz) { +#if AMI_KDTREE_USE_REAL_MEDIAN + return real_median(sz); +#else + int i = 0; + while ((1 << i) < ((sz + params_.leaf_size_max - 1) / + params_.leaf_size_max)) + i++; + return (1 << (i-1)) * params_.leaf_size_max - 1; +#endif + } + + TPIE_OS_SIZE_T max_intranode_height(AMI_bid bid) { + return (bid == header_.root_bid ? + params_.max_intraroot_height: + params_.max_intranode_height); + } + + // Used during binary bulk loading to pass parameters in the recursion. + class bn_context { + public: + bn_context() {} + bn_context(TPIE_OS_SIZE_T _i, TPIE_OS_SIZE_T _h, TPIE_OS_SIZE_T _d): + i(_i), h(_h), d(_d) {} + TPIE_OS_SIZE_T i; // the index of the current bin node. + TPIE_OS_SIZE_T h; // the depth (height) of the current bin node. + TPIE_OS_SIZE_T d; // the split dimension of the current bin node. + }; + + // Forward reference. + class grid; + + // The grid matrix containing the cell counts of a sub-grid. + class grid_matrix { + public: + // The grid to which this matrix refers to. + grid* g; + // The number of strips in g spanned by this sub-grid. + TPIE_OS_SIZE_T gt[dim]; + // The coordinates of the grid lines relative to g. The real + // coordinates: g->l[i][gl[i]] + TPIE_OS_SIZE_T gl[dim]; + // The grid counts. It's an array of length sz (the number of cells). + TPIE_OS_SIZE_T* c; + // Total number of cells: gt[0] * gt[1] *...* gt[dim-1]. + TPIE_OS_SIZE_T sz; + // Total number of points represented by this sub-grid. + TPIE_OS_OFFSET point_count; + // The low and high coordinates. The boolean bit is false iff the + // value is unbounded on that dimension. +#if AMI_KDTREE_USE_EXACT_SPLIT + pair lo[dim]; + pair hi[dim]; +#else + pair lo[dim]; + pair hi[dim]; +#endif + + // Construct a grid_matrix. + grid_matrix(TPIE_OS_SIZE_T* tt, grid *gg) { + size_t i; + sz = 1; + for (i = 0; i < dim; i++) { + gt[i] = tt[i]; + gl[i] = 0; + sz *= gt[i]; + lo[i].second = false; + hi[i].second = false; + } + g = gg; + point_count = g->point_count; + c = NULL; + } + + // Copy constructor. + grid_matrix(const grid_matrix& x) { + size_t i; + sz = 1; + for (i = 0; i < dim; i++) { + gt[i] = x.gt[i]; + gl[i] = x.gl[i]; + lo[i] = x.lo[i]; + hi[i] = x.hi[i]; + } + sz = x.sz; + g = x.g; + c = NULL; + } + + // Split along strip s orthogonal to dimension d. The lower + // coordinates are kept here, and the high ones are returned in a + // newly created object. + grid_matrix* split(TPIE_OS_SIZE_T s, const point_t& p, TPIE_OS_SIZE_T d) { + TPLOG(" ::grid_matrix::split Entering\n"); + + assert(d < dim); + assert(s < gt[d]); + TPIE_OS_SIZE_T i, j, ni; + + // The high matrix will be returned in gmx. + grid_matrix* gmx = new grid_matrix(*this); + // The no. of strips is the same on all dimensions except d. + gmx->gt[d] = gt[d] - s; + // The grid lines are the same on all dimensions except d, where + // we have to advance the pointer by s. + gmx->gl[d] = gl[d] + s; + + // Splitting the count matrix is much trickier. All this uglyness + // should be highly optimized by the compiler for 2 dimensions. + + // Multipliers for this matrix. + size_t mult[dim+1]; // mult[i] = t[0] * .. * t[i-1] + mult[0] = 1; + for (i = 1; i <= dim; i++) + mult[i] = mult[i-1] * gt[i-1]; + assert(mult[dim] == sz); + TPLOG(" initial size: "<gt[i-1]; + // The high matrix. + gmx->c = new TPIE_OS_SIZE_T[hi_mult[dim]]; + // The size of gmx. + gmx->sz = hi_mult[dim]; + TPLOG(" high size: "<sz<<"\n"); + + // i is i_0*mult[0] + i_1*mult[1] + ... + // For each element in the matrix, decide whether it goes in the + // low or in the high matrix. + for (i = 0; i < mult[dim]; i++) { + ni = 0; + + // Check on which side of strip s the cell given by i falls. Don't + // need the ones on the boundary strip, since those will get new + // values later. + if ((i % mult[d+1]) / mult[d] < s) { + // Compute the index in the lo_c array. + for (j = 0; j < dim; j++) + ni += ((i % mult[j+1]) / mult[j]) * lo_mult[j]; + // Fill in the corresponding position in lo_c. + lo_c[ni] = c[i]; + } else if ((i % mult[d+1]) / mult[d] > s) { + // Compute the index in the gmx->c array. + for (j = 0; j < dim; j++) + ni += ((i % mult[j+1]) / mult[j] - (j==d ? s: 0)) * hi_mult[j]; + // Fill in the corresponding position in the gmx->c array. + gmx->c[ni] = c[i]; + } else { // boundary. initialize these to 0. + // Compute the index position in the lo_c array. + for (j = 0; j < dim; j++) + ni += ((i % mult[j+1]) / mult[j]) * lo_mult[j]; + // Initialize to 0. + lo_c[ni] = 0; + + ni = 0; + // Compute the index position in the gmx->c array. + for (j = 0; j < dim; j++) + ni += ((i % mult[j+1]) / mult[j] - (j==d ? s: 0)) * hi_mult[j]; + // Initialize to 0. + gmx->c[ni] = 0; + } + } + + delete [] c; + c = lo_c; + + AMI_err err; + point_t* p1; + TPIE_OS_SIZE_T i_i, m; + TPIE_OS_OFFSET median_strip = gl[d] + s; // refers to the big grid! + TPLOG(" median strip in grid: "<, bool>(p, true); + gmx->lo[d] = pair, bool>(p, true); +#else + hi[d] = pair(p[d], true); + gmx->lo[d] = pair(p[d], true); +#endif + TPIE_OS_OFFSET off = g->o[d][median_strip]; + TPLOG(" stream offset of first pnt in median strip: "<streams[d]->seek(off); + + // Compute the counts for the cells on the rightmost strip in this + // matrix and for the cells on the leftmost strip in the gmx matrix. + + while ((err = g->streams[d]->read_item(&p1)) == AMI_ERROR_NO_ERROR) { + // Stop when reaching the offset of the next strip. + if (median_strip < g->t[d] - 1 && off >= g->o[d][median_strip + 1]) + break; + + // This test is using the new values for hi. + if (is_inside(*p1)) { + m = 1; + ni = 0; + for (i = 0; i < dim; i++) { + i_i = upper_bound(g->l[i] + gl[i], g->l[i] + (gl[i] + gt[i]-1), (*p1)[i]) + - (g->l[i] + gl[i]); + // On dimension d, we should have s. + assert(i != d || i_i == s); + assert(i_i < gt[i]); + ni += i_i * m; + m *= gt[i]; + } + assert(ni < sz); + c[ni]++; + assert(!gmx->is_inside(*p1)); + } + if (gmx->is_inside(*p1)) { + m = 1; + ni = 0; + for (i = 0; i < dim; i++) { + i_i = upper_bound(g->l[i] + gmx->gl[i], g->l[i] + (gmx->gl[i] + gmx->gt[i]-1), (*p1)[i]) + - (g->l[i] + gmx->gl[i]); + // On dimension d, we should have 0. + assert(i != d || i_i == 0); + assert(i_i < gmx->gt[i]); + ni += i_i * m; + m *= gmx->gt[i]; + } + assert(ni < gmx->sz); + gmx->c[ni]++; + assert(!is_inside(*p1)); + } + off++; + } + TPLOG(" ::grid_matrix::split Exiting\n"); + return gmx; + } + + + // Find the median point, store it in p, split according to the + // median point, and return the "high" sub-grid. + grid_matrix* find_median_and_split(point_t& p, TPIE_OS_SIZE_T d, TPIE_OS_OFFSET median_pos) { + TPLOG(" ::grid_matrix::find_median_and_split Entering dim="<= median_pos + 1) + break; + else + acc += strip_count[i]; + } + assert(acc < point_count); + assert(acc <= median_pos); + assert(i < gt[d]); + // Store the index of the median strip in s. + s = i; + + TPIE_OS_OFFSET offset_in_strip = median_pos - acc; // refers to this subgrid. + assert(offset_in_strip < strip_count[s]); + delete [] strip_count; + + TPLOG(" median strip: "<streams[d]->seek(g->o[d][gl[d]+s]); + err = g->streams[d]->read_item(&p1); + + while (err == AMI_ERROR_NO_ERROR) { + assert((*p1)[d] >= g->l[d][gl[d]+(s-1)]); + assert((*p1)[d] < g->l[d][gl[d]+s]); + + if (is_inside(*p1)) { + if (i == offset_in_strip) { + ap = *p1; + break; + } + // Count only points inside. + i++; + } + err = g->streams[d]->read_item(&p1); + } + + assert(i == offset_in_strip); + TPLOG(" preliminary median point: ("<streams[d]->read_item(&p1); + +#if AMI_KDTREE_USE_EXACT_SPLIT + while (err == AMI_ERROR_NO_ERROR && ap == (*p1)) { +#else + // Keep reading until the coordinate on dimension d is different. + while (err == AMI_ERROR_NO_ERROR && ap[d] == (*p1)[d]) { +#endif + if (is_inside(*p1)) { + offset_in_strip++; + ap = *p1; + TPLOG(" advanced offset_in_strip. new point: ("<streams[d]->read_item(&p1); + } + + // The point we are looking for is ap. + + TPLOG(" new offset in median strip: "<point_count = point_count - (offset_in_strip + acc + 1); + TPLOG(" high matrix point count: "<point_count<<"\n"); + + // Update point_count for the low matrix. + point_count = offset_in_strip + acc + 1; + TPLOG(" low matrix point count: "< 0) { + ans = false; + break; + } +#else + if (lo[i].second && p[i] <= lo[i].first) { + ans = false; + break; + } else if (hi[i].second && p[i] > hi[i].first) { + ans = false; + break; + } +#endif + } + return ans; + } + + // Destructor. Delete c. + ~grid_matrix() { + delete [] c; + } + }; + + class grid_context { + public: + AMI_bid bid; + bn_context ctx; + stream_t* streams[dim]; + char *stream_names[dim]; + bool low; + +#define NEW_DISTRIBUTE_G 1 +#if NEW_DISTRIBUTE_G + grid_matrix gmx; +#endif + + grid_context(AMI_bid _bid, bn_context _ctx, bool _low, + const grid_matrix& _gmx): + bid(_bid), ctx(_ctx), low(_low), gmx(_gmx) {} + }; + + // The grid info for the new bulk loading alg. + class grid { + public: + // The number of strips on each dimension. To avoid confusion: the + // tic-tac-toe board has t[0]=t[1]=3. There should be at least 2 + // strips on each dimension. The leftmost and rightmost strips on + // each dimension are unbounded. + TPIE_OS_SIZE_T t[dim]; + // Pointer to an array of dim streams containing the points. These + // are neither initialized nor destroyed here. + stream_t** streams; + // The coordinates of the grid lines. l[i] is an array of length + // t[i]-1. + coord_t* l[dim]; + // o[i][j] is the offset in streams[i] of the point that defines + // grid line l[i][j-1]. o[i] is an array of length t[i]. + TPIE_OS_OFFSET *o[dim]; + TPIE_OS_OFFSET point_count; + // The queue of unfinished business. + vector q; + + // Constructor. + grid(TPIE_OS_SIZE_T t_all, stream_t** in_streams) { + + streams = in_streams; + + point_count = in_streams[0]->stream_len(); + TPIE_OS_SIZE_T i, j; + TPIE_OS_OFFSET off; + AMI_record *p1, ap; + AMI_err err; + + // Determine the grid lines. + for (i = 0; i < dim; i++) { + t[i] = t_all; + l[i] = new coord_t[t[i] - 1]; + o[i] = new TPIE_OS_OFFSET[t[i]]; + assert(point_count > 2 * t[i]); // TODO: make this more meaningful. + o[i][0] = 0; + for (j = 0; j < t[i]-1; j++) { + off = (j + 1) * (point_count / t[i]) - 1; + in_streams[i]->seek(off); + err = in_streams[i]->read_item(&p1); + assert(err == AMI_ERROR_NO_ERROR); + ap = *p1; + err = in_streams[i]->read_item(&p1); + while (err == AMI_ERROR_NO_ERROR && (*p1)[i] == ap[i]) { + ap = *p1; + err = in_streams[i]->read_item(&p1); + off++; + } + assert(err == AMI_ERROR_NO_ERROR); + // The first point with a different value on the i'th dimension. + l[i][j] = (*p1)[i]; + o[i][j+1] = off + 1; + } + } + } + + + // Destructor + ~grid() { + size_t i; + for (i = 0; i < dim; i++) { + delete [] l[i]; + delete [] o[i]; + } + q.clear(); + // assert(q.size() == 0); + } + + + grid_matrix* create_matrix() { + + size_t i, j; + AMI_err err; + + size_t len, half; + coord_t* middle, *first, val; + + grid_matrix* gmx = new grid_matrix(t, this); + + gmx->c = new TPIE_OS_SIZE_T[gmx->sz]; + for (j = 0; j < gmx->sz; j++) + gmx->c[j] = 0; + + size_t mult = 1; // multiplier. + size_t ni = 0, i_i, i_0 = 0; + point_t* p2; + coord_t oldvalue; + + streams[0]->seek(0); + err = streams[0]->read_item(&p2); + oldvalue = (*p2)[0]; + + // Compute the counts. Loop over all points. + while (err == AMI_ERROR_NO_ERROR) { + + // Since streams[0] is sorted on the first dimension, there's no + // need for binary search on this dimension. + if (i_0 < t[0] - 1) + if (l[0][i_0] == (*p2)[0] && (*p2)[0] > oldvalue) { + i_0++; + oldvalue = (*p2)[0]; + } + ni = i_0; + /// mult = t[0]; + mult = 1; + + for (i = 1; i < dim; i++) { + val = (*p2)[i]; + mult *= t[i-1]; /// + // Do binary search with (*p2)[i] over the lines in l[i] to + // find the i'th coordinate. + + /// i_i = upper_bound(l[i], l[i] + (t[i]-1), (*p2)[i]) - l[i]; + // START New code, taken from the stl library (stl_algo.h). + len = t[i]-1; + first = l[i]; + while (len > 0) { + half = len >> 1; + middle = first + half; + if (val < *middle) + len = half; + else { + first = middle + 1; + len -= half + 1; + } + } + i_i = first - l[i]; + // END New code. + + assert(i_i < t[i]); + ni += i_i * mult; + /// mult *= t[i]; + } + // assert(ni < gmx->sz); + gmx->c[ni]++; + err = streams[0]->read_item(&p2); + } + return gmx; + } + + }; + + + // Used by the sample bulk loader. Similar to grid_context. + class sample_context { + public: + AMI_bid bid; + bn_context ctx; + bool low; + stream_t* stream; + char *stream_name; + + sample_context(AMI_bid _bid, bn_context _ctx, bool _low): + bid(_bid), ctx(_ctx), low(_low) {} + }; + + // Sample info for the sample-based bulk loader. + class sample { + public: + // Pointer to the input stream. + stream_t* in_stream; + // The in-memory streams containing the sampled points. + point_t* mm_streams[dim]; + // The number of sample points. + TPIE_OS_SIZE_T sz; + // The queue of unfinished business. + vector q; + + // Construct a sample. + sample(TPIE_OS_SIZE_T _sz, stream_t* _in_stream) { +#define MAX_RANDOM ((double)0x7fffffff) + + // Preliminary sample size. + sz = _sz; + in_stream = _in_stream; + TPIE_OS_OFFSET input_sz = in_stream->stream_len(); + assert(sz > 0 && sz < input_sz); + + TPIE_OS_OFFSET* offsets = new TPIE_OS_OFFSET[sz]; + TPIE_OS_OFFSET* new_last; + point_t *p; + TPIE_OS_SIZE_T i; + + TPIE_OS_SRANDOM(10); + + // Sample sz offsets in the interval [0, input_sz]. + for (i = 0; i < sz; i++) { + offsets[i] = TPIE_OS_OFFSET((TPIE_OS_RANDOM()/MAX_RANDOM) * input_sz); + } + + // Sort the sampled offsets. + std::sort(offsets, offsets + sz); + + // Eliminate duplicates. + if ((new_last = unique(offsets, offsets + sz)) != offsets + sz) { + cerr << " Warning: Duplicate samples found! Decreasing sample size accordingly.\n"; + // Adjust sample size sz. + sz = new_last - offsets; + cerr << " New sample size: " << (TPIE_OS_OFFSET)sz << "\n"; + } + + // Make space for one in-memory array (more later). + mm_streams[0] = new point_t[sz]; + + // Read the sample points. + for (i = 0; i < sz; i++) { + assert(offsets[i] < input_sz); + in_stream->seek(offsets[i]); + in_stream->read_item(&p); + mm_streams[0][i] = *p; + } + + // Delete the offsets array. + delete [] offsets; + + // Make space for the other (d-1) in-memory arrays and copy the + // points from the existing array. + for (i = 1; i < dim; i++) { + mm_streams[i] = new point_t[sz]; + for (size_t j = 0; j < sz; j ++) + mm_streams[i][j] = mm_streams[0][j]; + } + + // Sort the d in-memory arrays on each dimension. + typename point_t::cmp* comp_obj; + for (i = 0; i < dim; i++) { + comp_obj = new typename point_t::cmp(i); + quick_sort_obj(mm_streams[i], sz, comp_obj); + delete comp_obj; + } + } + + // Remove the sample points. + void cleanup() { + size_t i; + for (i = 0; i < dim; i++) { + delete [] mm_streams[i]; + mm_streams[i] = NULL; + } + } + + ~sample() { + cleanup(); + } + }; + + // Pair of dim flags. Used in window_query. + struct podf { + bool first[dim]; + bool second[dim]; + bool alltrue() { + for (size_t i = 0; i < dim; i++) { + if (!first[i] || !second[i]) + return false; + } + return true; + } + }; + + typedef pair > outer_stack_elem; + typedef pair inner_stack_elem; + + // Used for printing the binary kd-tree. + // An element represents a binary kd-tree + // node and the number of times it was visited. + // bid id the block id of the block node, and idx is the index + // of the bin node (or -1 for a leaf node). + // lo and hi form the mbr of the node. + struct print_stack_elem { + AMI_bid bid; + int idx; + int visits; + point_t lo; + point_t hi; + print_stack_elem(AMI_bid _bid, int _idx, int _visits, point_t _lo, point_t _hi): bid(_bid), idx(_idx), visits(_visits), lo(_lo), hi(_hi) {} + }; + + // Used for nearest neighbor searching. + struct nn_pq_elem { + double p; // the priority (the distance squared) + AMI_bid bid; + link_type_t type; + }; + + // Helpers for binary distribution bulk loading. + void create_bin_node(node_t *b, bn_context ctx, + stream_t** in_streams, + size_t& next_free_el, size_t& next_free_lk); + void create_node(AMI_bid& bid, TPIE_OS_SIZE_T d, + stream_t** in_streams); + void create_leaf(AMI_bid& bid, TPIE_OS_SIZE_T d, + stream_t** in_streams); + + // Helpers for in-memory bulk loading. + bool can_do_mm(TPIE_OS_OFFSET sz); + // Copy the given sorted streams into newly created in-memory + // streams. The input streams are deleted. + void copy_to_mm(stream_t** in_streams, + point_t** mm_streams, TPIE_OS_SIZE_T& sz); + // Copy the given stream in memory, make dim copy of it, and sort + // them on the dim dimensions. + void copy_to_mm(stream_t* in_stream, + point_t** mm_streams, TPIE_OS_SIZE_T& sz); + void create_bin_node_mm(node_t *b, bn_context ctx, + point_t** in_streams, TPIE_OS_SIZE_T sz, + size_t& next_free_el, size_t& next_free_lk); + void create_node_mm(AMI_bid& bid, TPIE_OS_SIZE_T d, + point_t** in_streams, TPIE_OS_SIZE_T sz); + void create_leaf_mm(AMI_bid& bid, TPIE_OS_SIZE_T d, + point_t** in_streams, TPIE_OS_SIZE_T sz); + + // Helpers for grid-based bulk loading. + void create_bin_node_g(node_t *b, bn_context ctx, + grid_matrix *gmx, size_t& next_free_el, size_t& next_free_lk); + void create_node_g(AMI_bid& bid, TPIE_OS_SIZE_T d, grid_matrix* gmx); + void create_grid(AMI_bid& bid, TPIE_OS_SIZE_T d, stream_t** in_streams, TPIE_OS_SIZE_T t); + void distribute_g(AMI_bid bid, TPIE_OS_SIZE_T d, grid* g); + void build_lower_tree_g(grid* g); + + // Helpers for sample-based bulk loading. + bool points_are_sample; + // Global sample object. + sample* gso; + void create_sample(AMI_bid& bid, TPIE_OS_SIZE_T d, stream_t* in_stream); + void distribute_s(AMI_bid bid, TPIE_OS_SIZE_T d, sample* s); + void build_lower_tree_s(sample* s); + + // Find the leaf where p might be. + AMI_bid find_leaf(const point_t &p); + + // Fetch a node from cache or disk. If bid is 0, a new node is created. + node_t* fetch_node(AMI_bid bid = 0); + // Fetch a leaf. + leaf_t* fetch_leaf(AMI_bid bid = 0); + // Release a node (put it into the cache). + void release_node(node_t* q); + // Release a leaf. + void release_leaf(leaf_t* q); +}; + + +struct _AMI_kdtree_leaf_info { + TPIE_OS_SIZE_T size; + AMI_bid next; +#if AMI_KDTREE_USE_KDBTREE_LEAF + TPIE_OS_SIZE_T split_dim; +#endif +}; + +// A kdtree leaf is a block of AMI_point's. The info field contains the +// number of points actually stored (ie, the size) and the id of +// another leaf. All leaves in a tree are threaded this way. +template +class AMI_kdtree_leaf: public AMI_block, _AMI_kdtree_leaf_info, BTECOLL> +{ + public: + using AMI_block, _AMI_kdtree_leaf_info, BTECOLL>::info; + using AMI_block, _AMI_kdtree_leaf_info, BTECOLL>::el; + using AMI_block, _AMI_kdtree_leaf_info, BTECOLL>::dirty; + + typedef AMI_record point_t; + typedef AMI_STREAM stream_t; + typedef AMI_collection_single collection_t; + typedef _AMI_kdtree_leaf_info info_t; + + static TPIE_OS_SIZE_T el_capacity(TPIE_OS_SIZE_T block_size); + + AMI_kdtree_leaf(collection_t* pcoll, AMI_bid bid = 0); + + // Number of points stored in this leaf. + TPIE_OS_SIZE_T& size() { return info()->size; } + const TPIE_OS_SIZE_T& size() const { return info()->size; } + + // The weight of a leaf is the size. Just for symmetry with the + // nodes. + const TPIE_OS_OFFSET weight() const { return info()->size; } + + // Next leaf. All leaves of a tree are chained together for easy + // retrieval. + const AMI_bid& next() const { return info()->next; } + AMI_bid& next() { return info()->next; } + +#if AMI_KDTREE_USE_KDBTREE_LEAF + TPIE_OS_SIZE_T& split_dim() { return info()->split_dim; } + const TPIE_OS_SIZE_T& split_dim() const { return info()->split_dim; } +#endif + + // Maximum number of points that can be stored in this leaf. + TPIE_OS_SIZE_T capacity() const { return el.capacity(); } + + // Find a point. Return the index of the point found in the el + // vector (if not found, return size()). + TPIE_OS_SIZE_T find(const point_t &p) const; + + TPIE_OS_OFFSET window_query(const point_t &lop, + const point_t &hip, + stream_t* stream) const; + + // Insert a point, assuming the leaf is not full. + bool insert(const point_t &p); + + bool erase(const point_t &p); + + // Sort points on the given dimension. + void sort(TPIE_OS_SIZE_T d); + + // Find median point on the given dimension. Return the index of the + // median in the el vector. + TPIE_OS_SIZE_T find_median(TPIE_OS_SIZE_T d); +}; + + +struct _AMI_kdtree_node_info { + TPIE_OS_SIZE_T size; + TPIE_OS_OFFSET weight; +}; + +// A kdtree node is a block of binary kdtree nodes (of templated type +// Bin_node). The info field contains the number of Bin_node's +// actually stored and the weight of the node (ie, the number of +// points stored in the subtree rooted on this node). +template +class AMI_kdtree_node: public AMI_block { + public: + using AMI_block::info; + using AMI_block::el; + using AMI_block::lk; + using AMI_block::dirty; + + typedef AMI_record point_t; + typedef AMI_STREAM stream_t; + typedef AMI_collection_single collection_t; + + // Compute the capacity of the lk vector STATICALLY (but you have to + // give it the correct logical block size!). + static TPIE_OS_SIZE_T lk_capacity(TPIE_OS_SIZE_T block_size); + // Compute the capacity of the el vector STATICALLY. + static TPIE_OS_SIZE_T el_capacity(TPIE_OS_SIZE_T block_size); + + AMI_kdtree_node(collection_t* pcoll, AMI_bid bid = 0); + + // Number of binary nodes stored in this node. + TPIE_OS_SIZE_T& size() { return info()->size; } + const TPIE_OS_SIZE_T& size() const { return info()->size; } + + TPIE_OS_OFFSET& weight() { return info()->weight; } + const TPIE_OS_OFFSET weight() const { return info()->weight; } + + // Maximum number of binary nodes that can be stored in this node. + TPIE_OS_SIZE_T capacity() const { return el.capacity(); } + + // Find the child node that leads to p. The second + // entry in the pair tells whether that pointer is a leaf AMI_bid + // or a node AMI_bid. + pair find(const point_t &p) const; + + pair find_index(const point_t &p) const; +}; + + +//////////////////////////////////////////////////////////// +/////////////// ***Implementation*** //////////////// +//////////////////////////////////////////////////////////// + +#define DBG(msg) cerr << msg << flush + +// Shortcuts for my convenience. +#define AMI_KDTREE_LEAF AMI_kdtree_leaf +#define AMI_KDTREE_NODE AMI_kdtree_node +#define AMI_KDTREE AMI_kdtree +#define POINT AMI_record +#define POINT_STREAM AMI_STREAM< POINT > + +#define MEM_AVAIL +//((float)(MM_manager.memory_available() /(MM_manager.memory_limit()/1000)) / 10) +#define MEMDISPLAY_INIT +//cout<<"Avail. memory: "< +TPIE_OS_SIZE_T AMI_KDTREE_LEAF::el_capacity(TPIE_OS_SIZE_T block_size) { + return AMI_block, _AMI_kdtree_leaf_info, BTECOLL>::el_capacity(block_size, 0); +} + +//// *AMI_kdtree_leaf::AMI_kdtree_leaf* //// +template +AMI_KDTREE_LEAF::AMI_kdtree_leaf(AMI_collection_single* pcoll, AMI_bid bid): + AMI_block(pcoll, 0, bid) { + TPLOG("AMI_kdtree_leaf::AMI_kdtree_leaf Entering bid="< +TPIE_OS_SIZE_T AMI_KDTREE_LEAF::find(const POINT &p) const { + TPLOG("AMI_kdtree_leaf::find Entering bid="< +bool AMI_KDTREE_LEAF::insert(const POINT &p) { + TPLOG("AMI_kdtree_leaf::insert Entering "<<"\n"); + assert(size() < el.capacity()); + // TODO: do a find() here to check for duplicate point. + el[size()] = p; + size()++; + dirty() = 1; + TPLOG("AMI_kdtree_leaf::insert Exiting "<<"\n"); + return true; +} + +//// *AMI_kdtree_leaf::erase* //// +template +bool AMI_KDTREE_LEAF::erase(const POINT &p) { + TPLOG("AMI_kdtree_leaf::erase Entering "<<"\n"); + bool ans = false; + TPIE_OS_SIZE_T idx; + if ((idx = find(p)) < size()) { + if (idx < size() - 1) { + // Copy the last item indo pos idx. We could use el.erase() as + // well, but that's slower. Here order is not important. + el[idx] = el[size()-1]; + } + size()--; + ans = true; + dirty() = 1; + } + + TPLOG("AMI_kdtree_leaf::erase Exiting ans="< +TPIE_OS_OFFSET AMI_KDTREE_LEAF::window_query(const POINT &lop, const POINT &hip, + POINT_STREAM* stream) const { + TPLOG("AMI_kdtree_leaf::window_query Entering "<<"\n"); + TPIE_OS_SIZE_T i; + TPIE_OS_OFFSET result = 0; + for (i = 0; i < size(); i++) { + // Test on all dimensions. + if (lop < el[i] && el[i] < hip) { + result++; + if (stream != NULL) + stream->write_item(el[i]); + } + } + + TPLOG("AMI_kdtree_leaf::window_query Exiting count="< +void AMI_KDTREE_LEAF::sort(TPIE_OS_SIZE_T d) { + typename POINT::cmp cmpd(d); + std::sort(&el[0], &el[0] + size(), cmpd); +} + +//// *AMI_kdtree_leaf::find_median* //// +template +size_t AMI_KDTREE_LEAF::find_median(TPIE_OS_SIZE_T d) { + typename POINT::cmp cmpd(d); + sort(d); + size_t ans = (size() - 1) / 2; // preliminary median. + while ((ans+1 < size()) && cmpd.compare(el[ans], el[ans+1]) == 0) + ans++; + return ans; +} + + +//////////////////////////////// +//////// **AMI_kdtree_node** /////// +//////////////////////////////// + +//// *AMI_kdtree_node::lk_capacity* //// +template +size_t AMI_KDTREE_NODE::lk_capacity(size_t block_size) { + return (size_t) ((block_size - sizeof(pair) - + sizeof(AMI_bid)) / + (sizeof(Bin_node) + sizeof(AMI_bid)) + 1); +} + +//// *AMI_kdtree_node::el_capacity* //// +template +size_t AMI_KDTREE_NODE::el_capacity(size_t block_size) { + TPLOG("AMI_kdtree_node::el_capacity Entering\n"); + TPLOG(" AMI_block::el_capacity(block_size, lk_capacity(block_size))="<<(AMI_block::el_capacity(block_size, lk_capacity(block_size)))<<"\n"); + TPLOG(" (lk_capacity(block_size) - 1)="<<(lk_capacity(block_size) - 1)<<"\n"); + // Sanity check. Two different methods of computing the el capacity. + // [12/21/01: changed == into >= since I could fit one more element, but not one more link] + assert((AMI_block::el_capacity(block_size, lk_capacity(block_size))) >= (size_t) (lk_capacity(block_size) - 1)); + TPLOG("AMI_kdtree_node::el_capacity Exiting\n"); + return (size_t) (lk_capacity(block_size) - 1); +} + +//// *AMI_kdtree_node::AMI_kdtree_node* //// +template +AMI_KDTREE_NODE::AMI_kdtree_node(AMI_collection_single* pcoll, AMI_bid bid): + AMI_block(pcoll, + lk_capacity(pcoll->block_size()), bid) { + TPLOG("AMI_kdtree_node::AMI_kdtree_node Entering bid="< +inline pair AMI_KDTREE_NODE::find_index(const POINT &p) const { + TPLOG("AMI_kdtree_node::find_index Entering "<<"\n"); + TPIE_OS_SIZE_T idx1 = 0, idx2; // the root bin node is always in pos 0. + link_type_t idx_type = BIN_NODE; + while (idx_type == BIN_NODE) { + // assert(idx1 < el.capacity()); + if (el[idx1].discriminate(p.key) <= 0) + el[idx1].get_low_child(idx2, idx_type); + else + el[idx1].get_high_child(idx2, idx_type); + // Make sure we don't loop forever. + // assert(idx_type != BIN_NODE || idx2 != idx1); + idx1 = idx2; + } + TPLOG("AMI_kdtree_node::find_index Exiting "<<"\n"); + return pair(idx1, idx_type); +} + +//// *AMI_kdtree_node::find* //// +template +pair AMI_KDTREE_NODE::find(const POINT &p) const { + TPLOG("AMI_kdtree_node::find Entering bid="< ans = find_index(p); + // assert(ans.second != GRID_INDEX); + TPLOG("AMI_kdtree_node::find Exiting "<<"\n"); + return pair(lk[ans.first], ans.second); +} + + +////////////////////////////////////// +///////////// **AMI_kdtree** ///////////// +////////////////////////////////////// + + +//// *AMI_kdtree::AMI_kdtree* //// +template +AMI_KDTREE::AMI_kdtree(const AMI_kdtree_params& params) + : header_(), params_(params), points_are_sample(false) { + TPLOG("AMI_kdtree::AMI_kdtree Entering\n"); + + char *base_file_name = tpie_tempnam("AMI_KDTREE"); + name_ = base_file_name; + shared_init(base_file_name, AMI_WRITE_COLLECTION); + if (status_ == AMI_KDTREE_STATUS_VALID) { + persist(PERSIST_DELETE); + } + + TPLOG("AMI_kdtree::AMI_kdtree Exiting status="< +AMI_KDTREE::AMI_kdtree(const char *base_file_name, AMI_collection_type type, + const AMI_kdtree_params& params) + : header_(), params_(params), points_are_sample(false), name_(base_file_name) { + TPLOG("AMI_kdtree::AMI_kdtree Entering base_file_name="< +AMI_KDTREE::AMI_kdtree(const string& base_file_name, AMI_collection_type type, + const AMI_kdtree_params& params) + : header_(), params_(params), points_are_sample(false), name_(base_file_name) { + TPLOG("AMI_kdtree::AMI_kdtree Entering base_file_name="< +void AMI_KDTREE::shared_init(const char* base_file_name, AMI_collection_type type) { + TPLOG("AMI_kdtree::shared_init Entering "<<"\n"); + + status_ = AMI_KDTREE_STATUS_VALID; + + if (base_file_name == NULL) { + status_ = AMI_KDTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("NULL pointer passed to AMI_kdtree constructor."); + return; + } + + char collname[124]; + + // Open the two block collections. + strncpy(collname, base_file_name, 124 - 2); + strcat(collname, ".l"); + pcoll_leaves_ = new collection_t(collname, type, params_.leaf_block_factor); + + strncpy(collname, base_file_name, 124 - 2); + strcat(collname, ".n"); + pcoll_nodes_ = new collection_t(collname, type, params_.node_block_factor); + + if (!pcoll_nodes_->is_valid() || !pcoll_leaves_->is_valid()) { + status_ = AMI_KDTREE_STATUS_INVALID; + delete pcoll_nodes_; + delete pcoll_leaves_; + return; + } + + // Read the header info, if relevant. + if (pcoll_leaves_->size() != 0) { + // header_ = *((header_t *) pcoll_nodes_->user_data()); + memcpy((void *)(&header_), pcoll_nodes_->user_data(), sizeof(header_)); + if (header_.magic_number != AMI_KDTREE_HEADER_MAGIC_NUMBER) { + status_ = AMI_KDTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("Invalid magic number in kdtree file."); + delete pcoll_nodes_; + delete pcoll_leaves_; + return; + } + if (header_.store_weights != AMI_KDTREE_STORE_WEIGHTS) { + status_ = AMI_KDTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("Invalid kdtree. Mismatch for AMI_KDTREE_STORE_WEIGHTS."); + delete pcoll_nodes_; + delete pcoll_leaves_; + return; + } + if (header_.use_exact_split != AMI_KDTREE_USE_EXACT_SPLIT) { + status_ = AMI_KDTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("Invalid kdtree. Mismatch for AMI_KDTREE_USE_EXACT_SPLIT."); + delete pcoll_nodes_; + delete pcoll_leaves_; + return; + } + if (header_.use_kdbtree_leaf != AMI_KDTREE_USE_KDBTREE_LEAF) { + status_ = AMI_KDTREE_STATUS_INVALID; + TP_LOG_WARNING_ID("Invalid kdtree. Mismatch for AMI_KDTREE_USE_KDBTREE_LEAF."); + delete pcoll_nodes_; + delete pcoll_leaves_; + return; + } + if (header_.use_real_median != AMI_KDTREE_USE_REAL_MEDIAN) { + TP_LOG_WARNING_ID("Warning: Mismatch for AMI_KDTREE_USE_REAL_MEDIAN"); + } + // TODO: more sanity checks on the header. + } + + // Initialize the caches. + leaf_cache_ = new leaf_cache_t(params_.leaf_cache_size, 8); + node_cache_ = new node_cache_t(params_.node_cache_size, 8); + + // Give meaningful values to parameters, if necessary. + size_t leaf_capacity = AMI_KDTREE_LEAF::el_capacity(pcoll_leaves_->block_size()); + if (params_.leaf_size_max == 0 || params_.leaf_size_max > leaf_capacity) + params_.leaf_size_max = leaf_capacity; + TPLOG(" leaf_size_max="<block_size()); + if (params_.node_size_max == 0 || params_.node_size_max > node_capacity) + params_.node_size_max = node_capacity; + TPLOG(" node_size_max="<> i == 1) + break; + assert(i < 64); + params_.max_intranode_height = i + 1; + } + TPLOG(" max_intranode_height="<block_factor(); + params_.node_block_factor = pcoll_nodes_->block_factor(); + + // hack. (TODO: fix!) + first_leaf_id_ = 1; + + bin_node_count_ = 0; + + TPLOG("AMI_kdtree::shared_init Exiting "<<"\n"); +} + +//// *AMI_kdtree::create_bin_node* //// +template +void AMI_KDTREE::create_bin_node(AMI_KDTREE_NODE *b, bn_context ctx, + POINT_STREAM** in_streams, + size_t& next_free_el, size_t& next_free_lk) { + TPLOG("AMI_kdtree::create_bin_node Entering bid="<bid()<<", dim="<") + + POINT_STREAM* lo_streams[dim]; + POINT_STREAM* hi_streams[dim]; + + + POINT *p1, ap; + TPIE_OS_OFFSET len = in_streams[ctx.d]->stream_len(); + assert(len > params_.leaf_size_max); + TPLOG(" AMI_kdtree::create_bin_node in_len="<seek(median(len)); + + // Read the median point. No need to read further. All points with + // the same value on the d'th coordinate will go in the low stream + // when distributing. + AMI_err err = in_streams[ctx.d]->read_item(&p1); + assert(err == AMI_ERROR_NO_ERROR); + // Save it in ap. + ap = *p1; + + // b->el[ctx.i] is the binary node we're constructing. + b->el[ctx.i].initialize(p1->key, ctx.d); + TPLOG(" AMI_kdtree::create_bin_node discriminator="<<(*p1)[ctx.d]<<", dim="<seek(0); + // Create the new streams. + lo_streams[i] = new POINT_STREAM; + lo_streams[i]->persist(PERSIST_DELETE); + hi_streams[i] = new POINT_STREAM; + hi_streams[i]->persist(PERSIST_DELETE); + + // Distribute. + while ((err = in_streams[i]->read_item(&p1)) == AMI_ERROR_NO_ERROR) { +#if AMI_KDTREE_USE_EXACT_SPLIT + ((comp_obj_[ctx.d]->compare(*p1, ap) <= 0) ? lo_streams[i] : hi_streams[i])->write_item(*p1); +#else + ((b->el[ctx.i].discriminate(p1->key) <= 0) ? lo_streams[i] : hi_streams[i])->write_item(*p1); +#endif + } + + assert(err == AMI_ERROR_END_OF_STREAM); + + assert(lo_streams[i]->stream_len() < in_streams[i]->stream_len()); + TPLOG(" AMI_kdtree::create_bin_node lo_len="<stream_len()<<"\n"); + assert(hi_streams[i]->stream_len() < in_streams[i]->stream_len()); + TPLOG(" AMI_kdtree::create_bin_node hi_len="<stream_len()<<"\n"); + + // Remove the input stream. + delete in_streams[i]; + in_streams[i] = NULL; + } + + ///DBG("create_bin_node: p=(" << ap[0] << "," << ap[1] << ") d=" << ctx.d << " pc_lo=" << lo_streams[0]->stream_len() << " pc_hi=" << hi_streams[0]->stream_len() << "\n"); + + ///assert(lo_streams[0]->stream_len() % 2 == 0); + + // The recursive calls... + + // ...for the low child... +#if AMI_KDTREE_STORE_WEIGHTS + b->el[ctx.i].low_weight() = lo_streams[0]->stream_len(); +#endif + if (lo_streams[0]->stream_len() <= params_.leaf_size_max) { + + b->el[ctx.i].set_low_child(next_free_lk++, BLOCK_LEAF); + TPLOG(" b("<bid()<<")->el["<lk[next_free_lk - 1], (ctx.d + 1) % dim, lo_streams); + + } else if (can_do_mm(lo_streams[0]->stream_len())) { + + POINT* lo_streams_mm[dim]; + size_t lo_sz; + copy_to_mm(lo_streams, lo_streams_mm, lo_sz); + + if ((ctx.h + 1 >= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + + b->el[ctx.i].set_low_child(next_free_lk++, BLOCK_NODE); + TPLOG(" b("<bid()<<")->el["<lk[next_free_lk - 1], (ctx.d + 1) % dim, lo_streams_mm, lo_sz); + + } else { + + b->el[ctx.i].set_low_child(next_free_el++, BIN_NODE); + TPLOG(" b("<bid()<<")->el["<= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + + b->el[ctx.i].set_low_child(next_free_lk++, BLOCK_NODE); + create_node(b->lk[next_free_lk - 1], (ctx.d + 1) % dim, lo_streams); + + } else { + + b->el[ctx.i].set_low_child(next_free_el++, BIN_NODE); + create_bin_node(b, bn_context(next_free_el - 1, ctx.h + 1, (ctx.d + 1) % dim), + lo_streams, next_free_el, next_free_lk); + } + } + + + // ...and for the high child. +#if AMI_KDTREE_STORE_WEIGHTS + b->el[ctx.i].high_weight() = hi_streams[0]->stream_len(); +#endif + if (hi_streams[0]->stream_len() <= params_.leaf_size_max) { + + b->el[ctx.i].set_high_child(next_free_lk++, BLOCK_LEAF); + TPLOG(" b("<bid()<<")->el["<lk[next_free_lk - 1], (ctx.d + 1) % dim, hi_streams); + + } else if (can_do_mm(hi_streams[0]->stream_len())) { + + POINT* hi_streams_mm[dim]; + size_t hi_sz; + copy_to_mm(hi_streams, hi_streams_mm, hi_sz); + + if ((ctx.h + 1 >= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + b->el[ctx.i].set_high_child(next_free_lk++, BLOCK_NODE); + TPLOG(" b("<bid()<<")->el["<lk[next_free_lk - 1], (ctx.d + 1) % dim, hi_streams_mm, hi_sz); + + } else { + + b->el[ctx.i].set_high_child(next_free_el++, BIN_NODE); + TPLOG(" b("<bid()<<")->el["<= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + + b->el[ctx.i].set_high_child(next_free_lk++, BLOCK_NODE); + create_node(b->lk[next_free_lk - 1], (ctx.d + 1) % dim, hi_streams); + + } else { + + b->el[ctx.i].set_high_child(next_free_el++, BIN_NODE); + create_bin_node(b, bn_context(next_free_el - 1, ctx.h + 1, (ctx.d + 1) % dim), + hi_streams, next_free_el, next_free_lk); + } + } + + HEIGHTDISPLAY_OUT + TPLOG("AMI_kdtree::create_bin_node Exiting bid="<bid()<<"\n"); +} + + +//// *AMI_kdtree::create_node* //// +template +void AMI_KDTREE::create_node(AMI_bid& bid, TPIE_OS_SIZE_T d, + POINT_STREAM** in_streams) { + TPLOG("AMI_kdtree::create_node Entering dim="<bid(); + n->weight() = in_streams[0]->stream_len(); + + assert(d < dim); + assert(in_streams[0]->stream_len() > params_.leaf_size_max); + + bn_context ctx(0, 0, d); + size_t next_free_el = 1; // because the root bin node goes in pos 0. + size_t next_free_lk = 0; + + if (can_do_mm(in_streams[0]->stream_len())) { + + POINT* in_streams_mm[dim]; + size_t sz; + copy_to_mm(in_streams, in_streams_mm, sz); + create_bin_node_mm(n, ctx, in_streams_mm, sz, next_free_el, next_free_lk); + + } else + create_bin_node(n, ctx, in_streams, next_free_el, next_free_lk); + + n->size() = next_free_el; + bin_node_count_ += n->size(); + release_node(n); + + TPLOG("AMI_kdtree::create_node Exiting bid="< +void AMI_KDTREE::create_leaf(AMI_bid& bid, TPIE_OS_SIZE_T d, + POINT_STREAM** in_streams) { + TPLOG("AMI_kdtree::create_leaf Entering "<<"\n"); + HEIGHTDISPLAY_IN(" O ") + + // New leaf. + AMI_KDTREE_LEAF* l = fetch_leaf(); + bid = l->bid(); + assert(d < dim); + + in_streams[d]->seek(0); + assert(in_streams[d]->stream_len() <= params_.leaf_size_max); + + // We are constructing a leaf, so we know that we have + // little enough points to safely cast. + l->size() = (TPIE_OS_SIZE_T)in_streams[d]->stream_len(); + + if (previous_leaf_ == NULL) { + first_leaf_id_ = l->bid(); + } else { + previous_leaf_->next() = l->bid(); + release_leaf(previous_leaf_); + } + previous_leaf_ = l; + + // Copy points from stream to leaf. This should be an array copy, + // but we don't have the mechanism... + POINT *p; + size_t i; + for (i = 0; i < l->size(); i++) { + in_streams[d]->read_item(&p); + l->el[i] = *p; + } + + // Remove the input streams. + for (i = 0; i < dim; i++) { + delete in_streams[i]; + in_streams[i] = NULL; + } + + HEIGHTDISPLAY_OUT + TPLOG("AMI_kdtree::create_leaf Exiting bid="< +void AMI_KDTREE::create_bin_node_mm(AMI_KDTREE_NODE *b, bn_context ctx, + POINT** in_streams, TPIE_OS_SIZE_T sz, + size_t& next_free_el, size_t& next_free_lk) { + TPLOG("AMI_kdtree::create_bin_node_mm Entering bid="<bid()<<", dim="< params_.leaf_size_max); + POINT *p1, *p2, ap; + TPIE_OS_SIZE_T read_pos; + TPIE_OS_SIZE_T hi_j, lo_j, i, j; + + if (points_are_sample) + // Get the real median. There's no point in doing anything fancy + // when building on top of a sample. + read_pos = real_median(sz); + else + read_pos = median(sz); + + current_stream = in_streams[ctx.d]; + + // Read a point. + p1 = ¤t_stream[read_pos++]; + assert(read_pos < sz); + + // Read another point. + p2 = ¤t_stream[read_pos]; + + // Verify sorted order. + assert((*p1)[ctx.d] <= (*p2)[ctx.d]); + + ap = *p1; + + // Initialize the binary node we are constructing. + b->el[ctx.i].initialize(p1->key, ctx.d); + + // Keep reading until we can discriminate between the two points, + // in order to get the exact position. We need the exact position + // to be able to allocate memory. +#if AMI_KDTREE_USE_EXACT_SPLIT + while (read_pos < sz && comp_obj_[ctx.d]->compare(*p2, ap) == 0) + p2 = ¤t_stream[++read_pos]; +#else + while (read_pos < sz && b->el[ctx.i].discriminate(p2->key) == 0) + p2 = ¤t_stream[++read_pos]; +#endif + + if (read_pos == sz) { + // Take drastic measures. + read_pos = real_median(sz); + p1 = ¤t_stream[read_pos++]; + p2 = ¤t_stream[read_pos]; + ap = *p1; + b->el[ctx.i].initialize(p1->key, ctx.d); +#if AMI_KDTREE_USE_EXACT_SPLIT + while (read_pos < sz && comp_obj_[ctx.d]->compare(*p2, ap) == 0) + p2 = ¤t_stream[++read_pos]; +#else + while (read_pos < sz && b->el[ctx.i].discriminate(p2->key) == 0) + p2 = ¤t_stream[++read_pos]; +#endif + } + + // Hopefully, we didn't hit the end. (TODO: what happens if we do?) + assert(read_pos < sz); + assert(read_pos >= 1); + + lo_sz = read_pos; + + // Create the output streams by distributing the input streams. + for (i = 0; i < dim; i++) { + + lo_streams[i] = new POINT[lo_sz]; + hi_streams[i] = new POINT[sz - lo_sz]; + // Distribute. + lo_j = hi_j = 0; + for (j = 0; j < sz; j++) { +#if AMI_KDTREE_USE_EXACT_SPLIT + if (comp_obj_[ctx.d]->compare(in_streams[i][j], ap) <= 0) + lo_streams[i][lo_j++] = in_streams[i][j]; +#else + if (b->el[ctx.i].discriminate(in_streams[i][j].key) <= 0) + lo_streams[i][lo_j++] = in_streams[i][j]; +#endif + else + hi_streams[i][hi_j++] = in_streams[i][j]; + } + assert(lo_j == lo_sz); + assert(hi_j == sz - lo_sz); + delete [] in_streams[i]; + in_streams[i] = NULL; + } + + // The recursive calls... + + // ...for the low child... +#if AMI_KDTREE_STORE_WEIGHTS + b->el[ctx.i].low_weight() = lo_sz; +#endif + if (lo_sz <= params_.leaf_size_max) { + + if (points_are_sample) { + + b->el[ctx.i].set_low_child(gso->q.size(), GRID_INDEX); + gso->q.push_back(sample_context(b->bid(), ctx, true)); + for (int ii = 0; ii < dim; ii++) { + delete [] lo_streams[ii]; + lo_streams[ii] = NULL; + } + + } else { + b->el[ctx.i].set_low_child(next_free_lk++, BLOCK_LEAF); + TPLOG(" b("<bid()<<")->el["<lk[next_free_lk - 1], (ctx.d + 1) % dim, + lo_streams, lo_sz); + } + } else if ((ctx.h + 1 >= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + + b->el[ctx.i].set_low_child(next_free_lk++, BLOCK_NODE); + TPLOG(" b("<bid()<<")->el["<lk[next_free_lk - 1], (ctx.d + 1) % dim, + lo_streams, lo_sz); + + } else { + + b->el[ctx.i].set_low_child(next_free_el++, BIN_NODE); + TPLOG(" b("<bid()<<")->el["< 0); + TPLOG(" AMI_kdtree::create_bin_node_mm Mid-recursion bid="<bid()<<"\n"); + + // ...and for the high child. +#if AMI_KDTREE_STORE_WEIGHTS + b->el[ctx.i].high_weight() = sz - lo_sz; +#endif + if (sz - lo_sz <= params_.leaf_size_max) { + + if (points_are_sample) { + b->el[ctx.i].set_high_child(gso->q.size(), GRID_INDEX); + gso->q.push_back(sample_context(b->bid(), ctx, false)); + for (int ii = 0; ii < dim; ii++) { + delete [] hi_streams[ii]; + hi_streams[ii] = NULL; + } + } else { + b->el[ctx.i].set_high_child(next_free_lk++, BLOCK_LEAF); + TPLOG(" b("<bid()<<")->el["<lk[next_free_lk - 1], (ctx.d + 1) % dim, + hi_streams, sz - lo_sz); + } + } else if ((ctx.h + 1 >= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + + b->el[ctx.i].set_high_child(next_free_lk++, BLOCK_NODE); + TPLOG(" b("<bid()<<")->el["<lk[next_free_lk - 1], (ctx.d + 1) % dim, + hi_streams, sz - lo_sz); + + } else { + + b->el[ctx.i].set_high_child(next_free_el++, BIN_NODE); + TPLOG(" b("<bid()<<")->el["< 1); + HEIGHTDISPLAY_OUT; + TPLOG("AMI_kdtree::create_bin_node_mm Exiting bid="<bid()<<"\n"); +} + +//// *AMI_kdtree::create_leaf_mm* //// +template +void AMI_KDTREE::create_leaf_mm(AMI_bid& bid, TPIE_OS_SIZE_T d, + POINT** in_streams, TPIE_OS_SIZE_T sz) { + TPLOG("AMI_kdtree::create_leaf_mm Entering "<<"\n"); + + // Brand new leaf. + AMI_KDTREE_LEAF* l = fetch_leaf(); + bid = l->bid(); + assert(d < dim); + assert(sz <= params_.leaf_size_max); + + l->size() = sz; + + if (previous_leaf_ == NULL) { + first_leaf_id_ = l->bid(); + } else { + previous_leaf_->next() = l->bid(); + release_leaf(previous_leaf_); + } + previous_leaf_ = l; + + size_t i; + + // Copy the points. + l->el.copy(0, sz, in_streams[0]); + + // Remove the input streams. + for (i = 0; i < dim; i++) { + delete [] in_streams[i]; + in_streams[i] = NULL; + } + + TPLOG("AMI_kdtree::create_leaf_mm Exiting bid="< +void AMI_KDTREE::create_node_mm(AMI_bid& bid, TPIE_OS_SIZE_T d, + POINT** in_streams, TPIE_OS_SIZE_T sz) { + TPLOG("AMI_kdtree::create_node_mm Entering dim="<bid(); + n->weight() = sz; + + assert(d < dim); + assert(sz > params_.leaf_size_max); + + bn_context ctx(0, 0, d); + size_t next_free_el = 1; // because the root bin node goes in pos 0. + size_t next_free_lk = 0; + n->lk[n->lk.capacity()-1] = 0; + + create_bin_node_mm(n, ctx, in_streams, sz, next_free_el, next_free_lk); + + n->size() = next_free_el; + //.. + if (n->lk[n->lk.capacity()-1] == 0) + n->lk[n->lk.capacity()-1] = next_free_lk; + bin_node_count_ += n->size(); + release_node(n); + TPLOG("AMI_kdtree::create_node_mm Exiting bid="< +bool AMI_KDTREE::can_do_mm(TPIE_OS_OFFSET sz) { + bool ans = ((TPIE_OS_OFFSET) sz * sizeof(POINT) * TPIE_OS_OFFSET(dim + 1) + + pcoll_nodes_->block_size() * params_.node_cache_size + + pcoll_leaves_->block_size() * params_.leaf_cache_size + + TPIE_OS_OFFSET(8192 * 4) < (TPIE_OS_OFFSET) MM_manager.memory_available()); + TPLOG("AMI_kdtree::can_do_mm needed = " << + (TPIE_OS_OFFSET) ((TPIE_OS_OFFSET) sz * sizeof(POINT) * TPIE_OS_OFFSET(dim + 1) + + pcoll_nodes_->block_size() * params_.node_cache_size + + pcoll_leaves_->block_size() * params_.leaf_cache_size + + TPIE_OS_OFFSET(8192 * 4)) << ", avail = " << + MM_manager.memory_available() << " ans = " << ans << "\n"); + return ans; +} + +//// *AMI_kdtree::copy_to_mm* //// +template +void AMI_KDTREE::copy_to_mm(POINT_STREAM** in_streams, POINT** mm_streams, TPIE_OS_SIZE_T& sz) { + TPLOG("AMI_kdtree::copy_to_mm Entering "<<"\n"); + // The call to this method should have been preceeded by a call + // to can_do_mm, so casting should be o.k. + sz = (TPIE_OS_SIZE_T)in_streams[0]->stream_len(); + TPIE_OS_SIZE_T i, j; + POINT* p; + + for (i = 0; i < dim; i++) { + mm_streams[i] = new POINT[sz]; + in_streams[i]->seek(0); + j = 0; + while (in_streams[i]->read_item(&p) == AMI_ERROR_NO_ERROR) { + mm_streams[i][j++] = *p; + } + // Delete the stream. + delete in_streams[i]; + in_streams[i] = NULL; + } + + TPLOG("AMI_kdtree::copy_to_mm Exiting "<<"\n"); +} + +//// *AMI_kdtree::copy_to_mm* //// +template +void AMI_KDTREE::copy_to_mm(POINT_STREAM* in_stream, POINT** streams_mm, TPIE_OS_SIZE_T& sz) { + TPLOG("AMI_kdtree::copy_to_mm Entering "<<"\n"); + // This method call should have been preceeded by a call to can_do_mm + // so casting should be o.k. + sz = (TPIE_OS_SIZE_T)in_stream->stream_len(); + TPIE_OS_SIZE_T i, j; + POINT* p; + // bool set_mbr; + + // Read from disk into the first in-memory stream. + streams_mm[0] = new POINT[sz]; + in_stream->seek(0); + i = 0; + while (in_stream->read_item(&p) == AMI_ERROR_NO_ERROR) + streams_mm[0][i++] = *p; + // delete in_stream; + + // Make dim-1 more in-memory copies. + for (j = 1; j < dim; j++) { + streams_mm[j] = new POINT[sz]; + // memcpy(streams_mm[j], streams_mm[0], sz * sizeof(POINT)); + for (i = 0; i < sz; i++) + streams_mm[j][i] = streams_mm[0][i]; + } + + // Sort the dim in-memory streams (and update the mbr). + for (j = 0; j < dim; j++) { + + quick_sort_obj(streams_mm[j], sz, comp_obj_[j]); + + if (header_.mbr_lo.id() == 0 || header_.mbr_hi.id() == 0) { + header_.mbr_lo[j] = streams_mm[j][0][j]; + header_.mbr_hi[j] = streams_mm[j][sz-1][j]; + } else { + header_.mbr_lo[j] = min(streams_mm[j][0][j], header_.mbr_lo[j]); + header_.mbr_hi[j] = max(streams_mm[j][sz-1][j], header_.mbr_hi[j]); + } + } + + if (header_.mbr_lo.id() == 0 || header_.mbr_hi.id() == 0) { + header_.mbr_lo.id() = 1; + header_.mbr_hi.id() = 1; + } + + TPLOG("AMI_kdtree::copy_to_mm Exiting "<<"\n"); +} + +//// *AMI_kdtree::create_grid* //// +template +void AMI_KDTREE::create_grid(AMI_bid& bid, TPIE_OS_SIZE_T d, POINT_STREAM** in_streams, TPIE_OS_SIZE_T t) { + TPLOG("AMI_kdtree::create_grid Entering "<<"\n"); + // Note: only one grid level implemented. + + DBG(" Computing grid lines [" << (TPIE_OS_OFFSET)dim*t << " (random seek + read)]...\n"); + grid *g = new grid(t, in_streams); + + DBG(" Creating matrix [1 linear scan]...\n"); + grid_matrix* gmx = g->create_matrix(); + + // Create log(t) levels. + DBG(" Creating top levels [very few node writes]...\n"); + create_node_g(header_.root_bid, 0, gmx); + + // Distribute the points. + DBG(" Distributing in " << (TPIE_OS_OFFSET)g->q.size() << "x" << (TPIE_OS_OFFSET)dim << " streams [" << (TPIE_OS_OFFSET)dim << " linear scans, distribution writing]...\n"); + distribute_g(header_.root_bid, 0, g); + + DBG(" Building lower levels [" << (TPIE_OS_OFFSET)g->q.size() << "x" << (TPIE_OS_OFFSET)dim << " small linear scans, lots of block writes]...\n"); + build_lower_tree_g(g); + + delete g; + TPLOG("AMI_kdtree::create_grid Exiting "<<"\n"); +} + +//// *AMI_kdtree::build_lower_tree_g* //// +template +void AMI_KDTREE::build_lower_tree_g(grid* g) { + TPLOG("AMI_kdtree::build_lower_tree_g Entering "<<"\n"); + + grid_context *gc; + size_t sz, i, j; + // POINT* p; + POINT* streams_mm[dim]; + // AMI_err err; + TPIE_OS_SIZE_T next_free_el; + TPIE_OS_SIZE_T next_free_lk; + AMI_KDTREE_NODE* b; + + for (j = 0; j < g->q.size(); j++) { + + gc = &(g->q[j]); + b = fetch_node(gc->bid); + next_free_el = b->size(); + next_free_lk = (TPIE_OS_SIZE_T)b->lk[b->lk.capacity()-1]; + b->lk[b->lk.capacity()-1] = 0; + + // Create the streams and load them into memory. + DBG("L"); + for (i = 0; i < dim; i++) { + gc->streams[i] = new POINT_STREAM(gc->stream_names[i]); + gc->streams[i]->persist(PERSIST_DELETE); + if (gc->streams[i]->status() == AMI_STREAM_STATUS_INVALID) { + cerr << "AMI_kdtree bulk loading internal error.\n" + << "[invalid stream restored from file " + << gc->stream_names[i] << "].\n"; + cerr << "Aborting.\n"; + delete gc->streams[i]; + exit(1); + } + delete [] gc->stream_names[i]; + gc->stream_names[i] = NULL; + } + + copy_to_mm(gc->streams, streams_mm, sz); + + // Build the subtree. + DBG("B"<<(TPIE_OS_OFFSET)sz); + if (sz <= params_.leaf_size_max) { + + if (gc->low) + b->el[gc->ctx.i].set_low_child(next_free_lk++, BLOCK_LEAF); + else + b->el[gc->ctx.i].set_high_child(next_free_lk++, BLOCK_LEAF); + TPLOG(" b("<bid()<<")->el["<ctx.i<<"].?: ("<lk[next_free_lk - 1], (gc->ctx.d + 1) % dim, streams_mm, sz); + + } else if ((gc->ctx.h + 1 >= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + + if (gc->low) + b->el[gc->ctx.i].set_low_child(next_free_lk++, BLOCK_NODE); + else + b->el[gc->ctx.i].set_high_child(next_free_lk++, BLOCK_NODE); + TPLOG(" b("<bid()<<")->el["<ctx.i<<"].?: ("<lk[next_free_lk - 1], (gc->ctx.d + 1) % dim, streams_mm, sz); + + } else { + + if (gc->low) + b->el[gc->ctx.i].set_low_child(next_free_el++, BIN_NODE); + else + b->el[gc->ctx.i].set_high_child(next_free_el++, BIN_NODE); + TPLOG(" b("<bid()<<")->el["<ctx.i<<"].?: ("<ctx.h + 1, + (gc->ctx.d + 1) % dim), + streams_mm, sz, next_free_el, next_free_lk); + } + + // save the next_free_* info. + if (b->lk[b->lk.capacity()-1] == 0) { + b->size() = next_free_el; + b->lk[b->lk.capacity()-1] = next_free_lk; + } + + release_node(b); + DBG(" "); + } + TPLOG("AMI_kdtree::build_lower_tree_g Exiting "<<"\n"); +} + +//// *AMI_kdtree::distribute_g* //// +template +void AMI_KDTREE::distribute_g(AMI_bid bid, TPIE_OS_SIZE_T d, grid* g) { + TPLOG("AMI_KDTREE::distribute_g Entering\n"); + AMI_err err; + TPIE_OS_SIZE_T i, j, jj; + TPIE_OS_OFFSET sz; + POINT* p1; + + TPLOG(" Queue size: " << g->q.size()); + // Create all streams. Could we run out of memory here? + for (j = 0; j < g->q.size(); j++) { + for (i = 0; i < dim; i++) { + g->q[j].streams[i] = new POINT_STREAM; + g->q[j].streams[i]->name(&g->q[j].stream_names[i]); + g->q[j].streams[i]->persist(PERSIST_PERSISTENT); + } + } + +#if NEW_DISTRIBUTE_G + jj = 0; + sz = dim * g->streams[0]->stream_len(); + for (i = 0; i < dim; i++) { + g->streams[i]->seek(0); + while ((err = g->streams[i]->read_item(&p1)) == AMI_ERROR_NO_ERROR) { + if (jj % 200000 == 0) + DBG("\b\b\b"<q.size(); j++) { + if (g->q[j].gmx.is_inside(*p1)) { + break; + } + } + // All points must be distributed, since no leaves were created so far. + assert(j < g->q.size()); + + g->q[j].streams[i]->write_item(*p1); + } + } + DBG("\b\b\b \b\b\b"); + +#else + // The root of this sub-tree is *r. + AMI_KDTREE_NODE *n, *r = fetch_node(bid); + AMI_bid nbid; + grid_context* gc; + pair a; + + // Distribute points. + for (i = 0; i < dim; i++) { + g->streams[i]->seek(0); + while ((err = g->streams[i]->read_item(&p1)) == AMI_ERROR_NO_ERROR) { + // Find the index in the q vector. + a = r->find_index(*p1); + if (a.second == BLOCK_NODE) + nbid = r->lk[a.first]; + while (a.second == BLOCK_NODE) { + n = fetch_node(nbid); + a = n->find_index(*p1); + if (a.second == BLOCK_NODE) + nbid = n->lk[a.first]; + release_node(n); + } + // Make sure there are no leaves or other funny stuff. + assert(a.second == GRID_INDEX); + + gc = &g->q[a.first]; + gc->streams[i]->write_item(*p1); + } + } + + release_node(r); +#endif + + // Delete input streams. + for (i = 0; i < dim; i++) { + delete g->streams[i]; + g->streams[i] = NULL; + } + // Delete output streams. We'll read them later, one by one. + for (j = 0; j < g->q.size(); j++) { + for (i = 0; i < dim; i++) { + delete g->q[j].streams[i]; + g->q[j].streams[i] = NULL; + } + } + + TPLOG("AMI_KDTREE::distribute_g Exiting\n"); +} + +//// *AMI_kdtree::create_node_g* //// +template +void AMI_KDTREE::create_node_g(AMI_bid& bid, TPIE_OS_SIZE_T d, grid_matrix* gmx) { + TPLOG("AMI_kdtree::create_node_g Entering "<<"\n"); + + AMI_KDTREE_NODE *n = fetch_node(); + bid = n->bid(); + n->weight() = gmx->point_count; + + assert(d < dim); + + bn_context ctx(0, 0, d); + size_t next_free_el = 1; // because the root bin node goes in pos 0. + size_t next_free_lk = 0; + n->lk[n->lk.capacity()-1] = 0; + + create_bin_node_g(n, ctx, gmx, next_free_el, next_free_lk); + + n->size() = next_free_el; + // Store next_free_el and next_free_lk in n. + if (n->lk[n->lk.capacity()-1] == 0) { + n->lk[n->lk.capacity()-1] = next_free_lk; + } + bin_node_count_ += n->size(); + release_node(n); + + TPLOG("AMI_kdtree::create_node_g Exiting bid="< +void AMI_KDTREE::create_bin_node_g(AMI_KDTREE_NODE *b, bn_context ctx, grid_matrix *gmx, + size_t& next_free_el, size_t& next_free_lk) { + TPLOG("AMI_kdtree::create_bin_node_g Entering "<<"\n"); + + grid_matrix* gmx_hi; // gmx will act as gmx_lo. + + POINT p; + // Find the median and split the current matrix. + gmx_hi = gmx->find_median_and_split(p, ctx.d, median(gmx->point_count)); + // Initialize the binary node. + b->el[ctx.i].initialize(p.key, ctx.d); + + ///DBG("create_bin_node_g: p=(" << p[0] << "," << p[1] << ") d=" << ctx.d << " pc_lo=" << gmx->point_count << " pc_hi=" << gmx_hi->point_count << "\n"); + + ///assert(gmx->point_count % 2 == 0); +#define USE_GRID_MORE 0 + +#if USE_GRID_MORE + if (gmx->point_count > params_.leaf_size_max && + (ctx.h + 1 < max_intranode_height(b->bid())) && + next_free_el < params_.node_size_max) { + b->el[ctx.i].set_low_child(next_free_el++, BIN_NODE); + create_bin_node_g(b, bn_context(next_free_el - 1, ctx.h + 1, (ctx.d + 1) % dim), + gmx, next_free_el, next_free_lk); + } else +#endif +#if AMI_KDTREE_STORE_WEIGHTS + b->el[ctx.i].low_weight() = gmx->point_count; +#endif + if (can_do_mm(gmx->point_count)) { + // Put a marker in the tree and exit. The actual buiding will be done later. + + b->el[ctx.i].set_low_child(gmx->g->q.size(), GRID_INDEX); + // Push the current context into the grid queue. + gmx->g->q.push_back(grid_context(b->bid(), ctx, true, *gmx)); + delete gmx; + + } else { + + if ((ctx.h + 1 >= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + b->el[ctx.i].set_low_child(next_free_lk++, BLOCK_NODE); + create_node_g(b->lk[next_free_lk - 1], (ctx.d + 1) % dim, gmx); + } else { + b->el[ctx.i].set_low_child(next_free_el++, BIN_NODE); + create_bin_node_g(b, bn_context(next_free_el - 1, ctx.h + 1, (ctx.d + 1) % dim), + gmx, next_free_el, next_free_lk); + } + } + + +#if USE_GRID_MORE + if (gmx_hi->point_count > params_.leaf_size_max && + (ctx.h + 1 < max_intranode_height(b->bid())) && + next_free_el < params_.node_size_max) { + b->el[ctx.i].set_high_child(next_free_el++, BIN_NODE); + create_bin_node_g(b, bn_context(next_free_el - 1, ctx.h + 1, (ctx.d + 1) % dim), + gmx_hi, next_free_el, next_free_lk); + } else +#endif +#if AMI_KDTREE_STORE_WEIGHTS + b->el[ctx.i].high_weight() = gmx_hi->point_count; +#endif + if (can_do_mm(gmx_hi->point_count)) { + + b->el[ctx.i].set_high_child(gmx_hi->g->q.size(), GRID_INDEX); + // Push the current context into the grid queue. + gmx_hi->g->q.push_back(grid_context(b->bid(), ctx, false, *gmx_hi)); + delete gmx_hi; + + } else { + + if ((ctx.h + 1 >= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + b->el[ctx.i].set_high_child(next_free_lk++, BLOCK_NODE); + create_node_g(b->lk[next_free_lk - 1], (ctx.d + 1) % dim, gmx_hi); + } else { + b->el[ctx.i].set_high_child(next_free_el++, BIN_NODE); + create_bin_node_g(b, bn_context(next_free_el - 1, ctx.h + 1, (ctx.d + 1) % dim), + gmx_hi, next_free_el, next_free_lk); + } + } + + TPLOG("AMI_kdtree::create_bin_node_g Exiting bid="<bid()<<"\n"); +} + +//// *AMI_kdtree::sort* //// +template +AMI_err AMI_KDTREE::sort(POINT_STREAM* in_stream, POINT_STREAM* out_streams[]) { + TPLOG("AMI_kdtree::sort Entering"<<"\n"); + + if (in_stream == NULL) { + TP_LOG_WARNING_ID("Attempting to sort a NULL stream pointer. Sorting Aborted."); + return AMI_ERROR_OBJECT_INITIALIZATION; + } + + assert(in_stream->stream_len() > 0); + size_t i; + AMI_err err; + + for (i = 0; i < dim; i++) { + // If necessary, create temporary stream for points sorted on the + // ith coordinate. + if (out_streams[i] == NULL) { + out_streams[i] = new POINT_STREAM; + out_streams[i]->persist(PERSIST_DELETE); + } + + // Sort points on the ith coordinate. + err = AMI_sort(in_stream, out_streams[i], comp_obj_[i]); + if (err != AMI_ERROR_NO_ERROR) + break; + assert(in_stream->stream_len() == out_streams[i]->stream_len()); + + } + if (err != AMI_ERROR_NO_ERROR) { + TP_LOG_WARNING_ID("Sorting returned error."); + } + + TPLOG("AMI_kdtree::sort Exiting err="< +AMI_err AMI_KDTREE::load_sorted(POINT_STREAM* streams_s[], + float lfill, float nfill, int load_method) { + TPLOG("AMI_kdtree::load_sorted Entering"<<"\n"); + AMI_err err = AMI_ERROR_NO_ERROR; + + // Some error checking. + if (header_.size > 0) { + TP_LOG_WARNING_ID("AMI_kdtree already loaded. Nothing done in load."); + return AMI_ERROR_GENERIC_ERROR; + } + if (status_ == AMI_KDTREE_STATUS_INVALID) { + TP_LOG_WARNING_ID("AMI_kdtree is invalid. Nothing done in load."); + return AMI_ERROR_OBJECT_INITIALIZATION; + } + if (streams_s[0] == NULL) { + TP_LOG_WARNING_ID("Attempting to load with a NULL stream pointer. Aborted."); + return AMI_ERROR_OBJECT_INITIALIZATION; + } + + header_.size = streams_s[0]->stream_len(); + first_leaf_id_ = 0; + previous_leaf_ = NULL; + + // Set max_intraroot_height. + //if (params_.max_intranode_height == params_.max_intraroot_height) + // params_.max_intraroot_height = + // min((size_t) (log((double)header_.size/params_.leaf_size_max)/log(2)) + // % params_.max_intranode_height + 1, params_.max_intranode_height); + + AMI_kdtree_params params_saved = params_; + params_.leaf_size_max = min(params_.leaf_size_max, + size_t(lfill*params_.leaf_size_max)); + params_.node_size_max = min(params_.node_size_max, + size_t(nfill*params_.node_size_max)); + + // Reinitialize params_.max_intranode_height + if (params_.max_intranode_height == params_.max_intraroot_height) { + // First reset intranode height. + size_t i; + for (i = 0; i < 64; i++) + if (params_.node_size_max >> i == 1) + break; + assert(i < 64); + params_.max_intranode_height = i + 1; + + // Now reset intraroot height. + params_.max_intraroot_height = + min((size_t) (log((double)header_.size/params_.leaf_size_max)/log(2.0)) + % params_.max_intranode_height + 1, params_.max_intranode_height); + } + + // Set the mbr. + POINT *pp; + size_t i; + for (i = 0; i < dim; i++) { + streams_s[i]->seek(0); + streams_s[i]->read_item(&pp); + header_.mbr_lo[i] = (*pp)[i]; + ///DBG("mbr_lo[" << i << "]=" << header_.mbr_lo[i] << "\n"); + streams_s[i]->seek(header_.size - 1); + streams_s[i]->read_item(&pp); + header_.mbr_hi[i] = (*pp)[i]; + streams_s[i]->seek(0); + ///DBG("mbr_hi[" << i << "]=" << header_.mbr_hi[i] << "\n"); + } + header_.mbr_lo.id() = 1; + header_.mbr_hi.id() = 1; + + + DBG("building (" << header_.size << ")...\n"); + MEMDISPLAY_INIT; + HEIGHTDISPLAY_INIT; + + // The actual loading. + if (header_.size <= params_.leaf_size_max) { + header_.root_type = BLOCK_LEAF; + create_leaf(header_.root_bid, 0, streams_s); + } else { + + header_.root_type = BLOCK_NODE; + if (can_do_mm(header_.size)) { + POINT* streams_mm[dim]; + TPIE_OS_SIZE_T sz; + copy_to_mm(streams_s, streams_mm, sz); + create_node_mm(header_.root_bid, 0, streams_mm, sz); + } else if (load_method & AMI_KDTREE_LOAD_BINARY) { + // Build the tree using binary distribution. + create_node(header_.root_bid, 0, streams_s); + } else if (load_method & AMI_KDTREE_LOAD_GRID) { + // Build the tree using the grid method. + create_grid(header_.root_bid, 0, streams_s, params_.grid_size); + } else { + TP_LOG_WARNING_ID("No other loading method implemented."); + TP_LOG_WARNING_ID("Loading aborted."); + err = AMI_ERROR_GENERIC_ERROR; + } + } + + status_ = AMI_KDTREE_STATUS_VALID; + + if (previous_leaf_ != NULL) { + previous_leaf_->next() = 0; + release_leaf(previous_leaf_); + } + + // Flush the caches. Not really necessary, but it helps free some + // memory. + node_cache_->flush(); + leaf_cache_->flush(); + + MEMDISPLAY_DONE; + HEIGHTDISPLAY_DONE; + + // Restore params_. + params_ = params_saved; + + TPLOG("AMI_kdtree::load_sorted Exiting err="< +AMI_err AMI_KDTREE::load(POINT_STREAM* s, float lfill, float nfill, int load_method) { + TPLOG("AMI_kdtree::load Entering "<<"\n"); + POINT_STREAM* streams_s[dim]; + size_t i; + AMI_err err; + + for (i = 0; i < dim; i++) { + streams_s[i] = NULL; + } + + // Sort. + err = sort(s, streams_s); + // Load. + if (err == AMI_ERROR_NO_ERROR) + err = load_sorted(streams_s, lfill, nfill, load_method); + + TPLOG("AMI_kdtree::load Exiting err="< +AMI_err AMI_KDTREE::load_sample(POINT_STREAM* s) { + TPLOG("AMI_kdtree::load_sample Entering"<<"\n"); + + AMI_err err = AMI_ERROR_NO_ERROR; + header_.size = s->stream_len(); + first_leaf_id_ = 0; + previous_leaf_ = NULL; + + // Set max_intraroot_height. + if (params_.max_intranode_height <= params_.max_intraroot_height) + params_.max_intraroot_height = + ((size_t) (log((double)header_.size/params_.leaf_size_max)/log(2.0)) + % params_.max_intranode_height + 1) % params_.max_intranode_height + 1; + + + if (header_.size <= params_.leaf_size_max) { + + header_.root_type = BLOCK_LEAF; + cerr << "Size too small. Not implemented.\n"; + err = AMI_ERROR_GENERIC_ERROR; + // create_leaf(header_.root_bid, 0, s); + + } else { + + header_.root_type = BLOCK_NODE; + if (can_do_mm(header_.size)) { + + POINT* streams_mm[dim]; + size_t sz; + copy_to_mm(s, streams_mm, sz); + create_node_mm(header_.root_bid, 0, streams_mm, sz); + + } else { + + // Build the tree using binary distribution. + create_sample(header_.root_bid, 0, s); + } + } + + status_ = AMI_KDTREE_STATUS_VALID; + + if (previous_leaf_ != NULL) { + previous_leaf_->next() = 0; + release_leaf(previous_leaf_); + } + + // Flush the caches. Not really necessary, but it helps free some + // memory. + node_cache_->flush(); + leaf_cache_->flush(); + + MEMDISPLAY_DONE; + HEIGHTDISPLAY_DONE + + TPLOG("AMI_kdtree::load_sample Exiting err="< +AMI_err AMI_KDTREE::unload(POINT_STREAM* s) { + TPLOG("AMI_kdtree::unload Entering "<<"\n"); + AMI_bid lid = first_leaf_id_; + AMI_KDTREE_LEAF *l; + size_t i; + AMI_err err = AMI_ERROR_NO_ERROR; + ///DBG(" first_leaf_id_=" << first_leaf_id_ << "\n"); + + if (s == NULL) { + TP_LOG_WARNING_ID(" unload: null stream pointer. unload aborted."); + return AMI_ERROR_OBJECT_INITIALIZATION; + } + if (status_ != AMI_KDTREE_STATUS_VALID) { + TP_LOG_WARNING_ID(" unload: tree is invalid or not loaded. unload aborted."); + return AMI_ERROR_OBJECT_INITIALIZATION; + } + assert(lid != 0); + + while (lid != 0) { + l = fetch_leaf(lid); + for (i = 0; i < l->size(); i++) + s->write_item(l->el[i]); + lid = l->next(); + release_leaf(l); + } + TPLOG("AMI_kdtree::unload Exiting err="< +TPIE_OS_OFFSET AMI_KDTREE::k_nn_query(const POINT &p, + POINT_STREAM* stream, TPIE_OS_OFFSET k) { + TPLOG("AMI_kdtree::k_nn_query Entering "<<"\n"); + TPIE_OS_OFFSET result = 0; + + // Do some error checking. + if (status_ != AMI_KDTREE_STATUS_VALID) { + TP_LOG_WARNING_ID(" k_nn_query: tree is invalid or not loaded. query aborted."); + return result; + } + + cerr << "k_nn_query: NOT IMPLEMENTED YET!\n"; + TP_LOG_WARNING_ID(" k_nn_query: NOT IMPLEMENTED YET!"); + // priority_queue q; + // nn_pq_elem cur((coord_t) 0, header_.root_bid, header_.root_type); + // while (cur.type != BLOCK_LEAF) { + // } + + TPLOG("AMI_kdtree::k_nn_query Exiting "<<"\n"); + return result; +} + +//// *AMI_kdtree::window_query* //// +template +TPIE_OS_OFFSET AMI_KDTREE::window_query(const POINT &p1, const POINT& p2, + POINT_STREAM* stream) { + TPLOG("AMI_kdtree::window_query Entering "<<"\n"); + POINT lop, hip; + // The number of points found. + TPIE_OS_OFFSET result = 0; + TPIE_OS_SIZE_T i; + + // Do some error checking. + if (status_ != AMI_KDTREE_STATUS_VALID) { + TP_LOG_WARNING_ID(" window_query: tree is invalid or not loaded. query aborted."); + return result; + } + + // Determine the low and high bounds of the box. + for (i = 0; i < dim; i++) { + lop[i] = min(p1[i], p2[i]); + hip[i] = max(p1[i], p2[i]); + if (p1[i] == p2[i]) + TP_LOG_WARNING_ID(" window_query: points have one identical coordinate."); + } + + // A stack for the search (no recursive calls here :). + stack s; + + // Another stack for the search inside a block node. The elements + // are indexes in the el vector of a block node. + stack ss; + + podf allfalse; + for (i = 0; i < dim; i++) { + allfalse.first[i] = false; // ie, low boundary of current box, on dim. i, is outside the query window. + allfalse.second[i] = false; // ie, high boundary on dim. i is outside the query window. + } + + s.push(outer_stack_elem(allfalse, + pair(header_.root_bid, header_.root_type))); + + pair top; + podf topflags, tempflags; + TPIE_OS_SIZE_T child; + link_type_t childtype; + AMI_KDTREE_NODE *bn, *bn2; + AMI_KDTREE_LEAF *bl; + + while (!s.empty()) { + // Copy the top of the stack. + top = s.top().second; + topflags = s.top().first; + s.pop(); + + if (top.second == BLOCK_LEAF) { + + bl = fetch_leaf(top.first); + result += bl->window_query(lop, hip, stream); + release_leaf(bl); + + } else { // BLOCK_NODE + + assert(top.second == BLOCK_NODE); + bn = fetch_node(top.first); + + // Inner stack should be empty. + assert(ss.empty()); + + // The first Bin_node in a block node always has index 0. + ss.push(inner_stack_elem(topflags, 0)); + + // The inner loop. Visit all relevant Bin_node's inside *bn. + while (!ss.empty()) { + Bin_node &v = bn->el[ss.top().second]; + // Recycle topflags. + topflags = ss.top().first; + ss.pop(); + + // Check whether we need to visit the low child of v. + if (v.discriminate(lop.key) <= 0 || v.discriminate(hip.key) <= 0) { + // Push the low child into the appropriate stack. + v.get_low_child(child, childtype); + // Make a copy of topflags. + tempflags = topflags; + + // Set the flag for the high boundary. + if (v.discriminate(lop.key) <= 0 && v.discriminate(hip.key) == 1) + tempflags.second[v.get_discriminator_dim()] = true; + + if (childtype == BLOCK_NODE) { + if (tempflags.alltrue() && stream == NULL) { + // No need to recurse, just return the weight of the child node. +#if AMI_KDTREE_STORE_WEIGHTS + result += v.low_weight(); +#else + bn2 = fetch_node(bn->lk[child]); + result += bn2->weight(); + release_node(bn2); +#endif + } else { + s.push(outer_stack_elem(tempflags, + pair(bn->lk[child], childtype))); + } + } else if (childtype == BLOCK_LEAF) { + if (tempflags.alltrue() && stream == NULL) { + // No need to recurse, just return the weight of the child node. +#if AMI_KDREE_STORE_WEIGHTS + result += v.low_weight(); +#else + bl = fetch_leaf(bn->lk[child]); + result += bl->weight(); + release_leaf(bl); +#endif + } else { + s.push(outer_stack_elem(tempflags, + pair(bn->lk[child], childtype))); + } + } else { // BIN_NODE +#if AMI_KDTREE_STORE_WEIGHTS + if (tempflags.alltrue() && stream == NULL) + result += v.low_weight(); + else + ss.push(inner_stack_elem(tempflags, child)); +#else + ss.push(inner_stack_elem(tempflags, child)); +#endif + } + } + + // Check whether we need to visit the high child of v. +#if AMI_KDTREE_USE_EXACT_SPLIT + if (v.discriminate(lop.key) >= 0 || v.discriminate(hip.key) >= 0) { +#else + if (v.discriminate(lop.key) == 1 || v.discriminate(hip.key) == 1) { +#endif + // Push the low child into the appropriate stack. + v.get_high_child(child, childtype); + // Again, make a copy of topflags. + tempflags = topflags; + + // Set the flag for the low boundary. +#if AMI_KDTREE_USE_EXACT_SPLIT + if (v.discriminate(lop.key) < 0 && v.discriminate(hip.key) >= 0) +#else + if (v.discriminate(lop.key) <= 0 && v.discriminate(hip.key) == 1) +#endif + tempflags.first[v.get_discriminator_dim()] = true; + + if (childtype == BLOCK_NODE) { + if (tempflags.alltrue() && stream == NULL) { + // No need to recurse, just return the weight of the child node. +#if AMI_KDTREE_STORE_WEIGHTS + result += v.high_weight(); +#else + bn2 = fetch_node(bn->lk[child]); + result += bn2->weight(); + release_node(bn2); +#endif + } else { + s.push(outer_stack_elem(tempflags, + pair(bn->lk[child], childtype))); + } + } else if (childtype == BLOCK_LEAF) { + if (tempflags.alltrue() && stream == NULL) { + // No need to recurse, just return the weight of the child node. +#if AMI_KDTREE_STORE_WEIGHTS + result += v.high_weight(); +#else + bl = fetch_leaf(bn->lk[child]); + result += bl->weight(); + release_leaf(bl); +#endif + } else { + s.push(outer_stack_elem(tempflags, + pair(bn->lk[child], childtype))); + } + } else { // BIN_NODE +#if AMI_KDTREE_STORE_WEIGHTS + if (tempflags.alltrue() && stream == NULL) + result += v.high_weight(); + else + ss.push(inner_stack_elem(tempflags, child)); +#else + ss.push(inner_stack_elem(tempflags, child)); +#endif + } + } + + } // while !ss.empty() + + // We are done with this block node. + release_node(bn); + } + + } // while !s.empty() + + TPLOG("AMI_kdtree::window_query Exiting "<<"\n"); + return result; +} + +//// *AMI_kdtree::find_leaf* //// +template +AMI_bid AMI_KDTREE::find_leaf(const POINT &p) { + TPLOG("AMI_kdtree::find_leaf Entering "<<"\n"); + pair n = + pair(header_.root_bid, header_.root_type); + AMI_KDTREE_NODE* bn; + // bool ans; + + // Go down the tree until the appropriate leaf is found. + while (n.second == BLOCK_NODE) { + bn = fetch_node(n.first); + n = bn->find(p); + release_node(bn); + } + + assert(n.second == BLOCK_LEAF); + TPLOG("AMI_kdtree::find_leaf Exiting "<<"\n"); + return n.first; +} + +//// *AMI_kdtree::find* //// +template +bool AMI_KDTREE::find(const POINT &p) { + + TPLOG("AMI_kdtree::find Entering "<<"\n"); + bool ans; + + // Check the leaf. + AMI_KDTREE_LEAF* bl = fetch_leaf(find_leaf(p)); + ans = (bl->find(p) < bl->size()); + release_leaf(bl); + + TPLOG("AMI_kdtree::find Exiting ans="< +bool AMI_KDTREE::insert(const POINT& p) { + TPLOG("AMI_kdtree::insert Entering "<<"\n"); + bool ans = false; + AMI_KDTREE_LEAF* bl = fetch_leaf(find_leaf(p)); + + if (bl->size() == params_.leaf_size_max) + ans = false; + else if (ans = bl->insert(p)) + header_.size++; + + // TODO: update the weights of all nodes on the path! + + TPLOG("AMI_kdtree::insert Exiting "<<"\n"); + return ans; +} + +//// *AMI_kdtree::erase* //// +template +bool AMI_KDTREE::erase(const POINT& p) { + TPLOG("AMI_kdtree::erase Entering "<<"\n"); + bool ans; + + AMI_KDTREE_LEAF* bl = fetch_leaf(find_leaf(p)); + if (ans = bl->erase(p)) + header_.size--; + release_leaf(bl); + + TPLOG("AMI_kdtree::erase Exiting "<<"\n"); + return ans; +} + +//// *AMI_kdtree::persist* //// +template +void AMI_KDTREE::persist(persistence per) { + TPLOG("AMI_kdtree::persist Entering "<<"\n"); + + pcoll_leaves_->persist(per); + pcoll_nodes_->persist(per); + + TPLOG("AMI_kdtree::persist Exiting "<<"\n"); +} + +//// *AMI_kdtree::fetch_node* //// +template +AMI_KDTREE_NODE* AMI_KDTREE::fetch_node(AMI_bid bid) { + AMI_KDTREE_NODE* q; + stats_.record(NODE_FETCH); + // Warning: using short-circuit evaluation. Order is important. + if ((bid == 0) || !node_cache_->read(bid, q)) { + q = new AMI_KDTREE_NODE(pcoll_nodes_, bid); + } + return q; +} + +//// *AMI_kdtree::fetch_leaf* //// +template +AMI_KDTREE_LEAF* AMI_KDTREE::fetch_leaf(AMI_bid bid) { + AMI_KDTREE_LEAF* q; + stats_.record(LEAF_FETCH); + // Warning: using short-circuit evaluation. Order is important. + if ((bid == 0) || !leaf_cache_->read(bid, q)) { + q = new AMI_KDTREE_LEAF(pcoll_leaves_, bid); + } + return q; +} + +//// *AMI_kdtree::release_node* //// +template +void AMI_KDTREE::release_node(AMI_KDTREE_NODE* q) { + stats_.record(NODE_RELEASE); + if (q->persist() == PERSIST_DELETE) + delete q; + else + node_cache_->write(q->bid(), q); +} + +//// *AMI_kdtree::release_leaf* //// +template +void AMI_KDTREE::release_leaf(AMI_KDTREE_LEAF* q) { + stats_.record(LEAF_RELEASE); + if (q->persist() == PERSIST_DELETE) + delete q; + else + leaf_cache_->write(q->bid(), q); +} + +//// *AMI_kdtree::stats* //// +template +const tpie_stats_tree &AMI_KDTREE::stats() { + node_cache_->flush(); + leaf_cache_->flush(); + stats_.set(LEAF_READ, pcoll_leaves_->stats().get(BLOCK_GET)); + stats_.set(LEAF_WRITE, pcoll_leaves_->stats().get(BLOCK_PUT)); + stats_.set(LEAF_CREATE, pcoll_leaves_->stats().get(BLOCK_NEW)); + stats_.set(LEAF_DELETE, pcoll_leaves_->stats().get(BLOCK_DELETE)); + stats_.set(LEAF_COUNT, pcoll_leaves_->size()); + stats_.set(NODE_READ, pcoll_nodes_->stats().get(BLOCK_GET)); + stats_.set(NODE_WRITE, pcoll_nodes_->stats().get(BLOCK_PUT)); + stats_.set(NODE_CREATE, pcoll_nodes_->stats().get(BLOCK_NEW)); + stats_.set(NODE_DELETE, pcoll_nodes_->stats().get(BLOCK_DELETE)); + stats_.set(NODE_COUNT, pcoll_nodes_->size()); + return stats_; +} + +//// *AMI_kdtree::print* //// +template +void AMI_KDTREE::print(ostream& s) { + s << "AMI_kdtree nodes: "; + if (header_.root_type == BLOCK_NODE) { + AMI_KDTREE_NODE* bn; + queue xq; // external queue; stores node id's + queue iq; // internal queue; + size_t i, idx, fo; + link_type_t idx_type; + + xq.push(header_.root_bid); + // level = 0; + while (!xq.empty()) { + bn = fetch_node(xq.front()); + xq.pop(); + assert(iq.empty()); + iq.push(0); + fo = 0; + + s << "[id=" << bn->bid() << " ("; + + while (!iq.empty()) { + i = iq.front(); + iq.pop(); + + s << "B" << (TPIE_OS_OFFSET)bn->el[i].get_discriminator_dim() << " " + << bn->el[i].get_discriminator_val() << "\n"; + + bn->el[i].get_low_child(idx, idx_type); + + if (idx_type == BIN_NODE) { + iq.push(idx); + } else { + fo++; + if (idx_type == BLOCK_NODE) { + s << "N" << bn->lk[idx]; + xq.push(bn->lk[idx]); + } else { + s << "L"; + } + s << " "; + } + + + bn->el[i].get_high_child(idx, idx_type); + + if (idx_type == BIN_NODE) { + iq.push(idx); + } else { + fo++; + if (idx_type == BLOCK_NODE) { + s << "N" << bn->lk[idx]; + xq.push(bn->lk[idx]); + } else { + s << "L"; + } + s << " "; + } + } + s << "\b) fo=" << (TPIE_OS_OFFSET)fo << "]\n"; + release_node(bn); + } + } else + s << " Root is leaf.\n"; + s << "\n"; +} + + +//// *AMI_kdtree::print* //// +template + void AMI_KDTREE::print(ostream& s, bool print_mbr, bool print_level, char indent_char) { + + + // s << "AMI_kdtree nodes: "; + if (header_.root_type == BLOCK_NODE) { + + AMI_KDTREE_NODE* bln; + AMI_KDTREE_LEAF* bll; + // The current binary node. + Bin_node bin; + + // The recursion stack. + stack dfs_stack; + + point_t rlo, rhi; + size_t i, j, idx, fo, level; + link_type_t idx_type; + + // Initialize the stack. + dfs_stack.push(print_stack_elem(header_.root_bid, 0, 0, header_.mbr_lo, header_.mbr_hi)); + + while (!dfs_stack.empty()) { + + rlo = dfs_stack.top().lo; + rhi = dfs_stack.top().hi; + + if (dfs_stack.top().idx == -1) { // Top of the stack is a leaf. + // Print the leaf. + + // The MBR. + if (print_mbr) { + s << "["; + s << "("; + for (j = 0; j < dim-1; j++) { + s << rlo[j] << ","; + } + s << rlo[dim-1] << ") "; + s << "("; + for (j = 0; j < dim-1; j++) { + s << rhi[j] << ","; + } + s << rhi[dim-1] << ")"; + s << "] "; + } + if (print_level) { + s << (dfs_stack.size()-1) << (dfs_stack.size()-1 < 10 ? " ": " "); + } + for (i = 0; i < dfs_stack.size()-1; i++) { + s << indent_char; + } + s << "L "; + bll = fetch_leaf(dfs_stack.top().bid); + for (i = 0; i < bll->size(); i++) { + s << "("; + for (j = 0; j < dim-1; j++) { + s << bll->el[i][j] << ","; + } + s << bll->el[i][dim-1] << ") "; + } + s << endl; + release_leaf(bll); + + dfs_stack.pop(); + + } else { // Top of the stack is a node. + + bln = fetch_node(dfs_stack.top().bid); + bin = bln->el[dfs_stack.top().idx]; + + if (dfs_stack.top().visits == 0) { + + // Print the binary node 'bin', since it's the first time we see it. + + // The MBR. + if (print_mbr) { + s << "["; + s << "("; + for (j = 0; j < dim-1; j++) { + s << rlo[j] << ","; + } + s << rlo[dim-1] << ") "; + s << "("; + for (j = 0; j < dim-1; j++) { + s << rhi[j] << ","; + } + s << rhi[dim-1] << ")"; + s << "] "; + } + if (print_level) { + s << (dfs_stack.size()-1) << (dfs_stack.size()-1 < 10 ? " ": " "); + } + for (i = 0; i < dfs_stack.size()-1; i++) { + s << indent_char; + } + + s << "B" << bin.get_discriminator_dim(); + s << " " << bin.get_discriminator_val(); + s << endl; + + bin.get_low_child(idx, idx_type); + rhi[bin.get_discriminator_dim()] = bin.get_discriminator_val(); + + } else { + bin.get_high_child(idx, idx_type); + rlo[bin.get_discriminator_dim()] = bin.get_discriminator_val(); + } + + dfs_stack.top().visits++; + + if (idx_type == BIN_NODE) { + dfs_stack.push(print_stack_elem(bln->bid(), idx, 0, rlo, rhi)); + } else if (idx_type == BLOCK_NODE) { + dfs_stack.push(print_stack_elem(bln->lk[idx], 0, 0, rlo, rhi)); + } else { // idx_type == BLOCK_LEAF + dfs_stack.push(print_stack_elem(bln->lk[idx], -1, 0, rlo, rhi)); + } + + release_node(bln); + + } + + while (!dfs_stack.empty() && dfs_stack.top().visits == 2) { + dfs_stack.pop(); + } + + } + + } else { + s << "Root is leaf." << endl; + } + + s << endl; +} + + + +//// *AMI_kdtree::~AMI_kdtree* //// +template +AMI_KDTREE::~AMI_kdtree() { + TPLOG("AMI_kdtree::~AMI_kdtree Entering status="<user_data()) = header_; + memcpy(pcoll_nodes_->user_data(), (void *)(&header_), sizeof(header_)); + } + // Delete the comparison objects. + for (size_t i = 0; i < dim; i++) { + delete comp_obj_[i]; + } + + delete node_cache_; + delete leaf_cache_; + + // Delete the two collections. + delete pcoll_nodes_; + delete pcoll_leaves_; + + TPLOG("AMI_kdtree::~AMI_kdtree Exiting status="< +void AMI_KDTREE::create_sample(AMI_bid& bid, TPIE_OS_SIZE_T d, POINT_STREAM* in_stream) { + + // New sample. + DBG(" Sampling [" << 20000 << " (random seek + read)]...\n"); + gso = new sample(20000, in_stream); + + DBG(" Creating top levels [very few node writes]...\n"); + // Dirty tricks. TODO: cleanup. + size_t save_leaf_size_max = params_.leaf_size_max; + points_are_sample = true; + params_.leaf_size_max = 5000; + while (!can_do_mm(size_t(((1.1 * in_stream->stream_len()) / gso->sz) * + params_.leaf_size_max))) + params_.leaf_size_max -= 50; + if (params_.leaf_size_max == 0) + params_.leaf_size_max = 40; + + create_node_mm(header_.root_bid, 0, gso->mm_streams, gso->sz); + params_.leaf_size_max = save_leaf_size_max; + points_are_sample = false; + gso->cleanup(); + + DBG(" Distributing into " << (TPIE_OS_OFFSET)gso->q.size() << " streams...\n"); + distribute_s(header_.root_bid, 0, gso); + + DBG(" Building lower levels...\n"); + build_lower_tree_s(gso); + + delete gso; +} + +//// *AMI_kdtree::build_lower_tree_s* //// +template +void AMI_KDTREE::build_lower_tree_s(sample* s) { + sample_context* sc; + size_t j; + // POINT* p; + AMI_KDTREE_NODE* b; + TPIE_OS_SIZE_T next_free_el, next_free_lk; + TPIE_OS_SIZE_T sz; + POINT* streams_mm[dim]; + + for (j = 0; j < s->q.size(); j++) { + + sc = &s->q[j]; + + b = fetch_node(sc->bid); + next_free_el = b->size(); + next_free_lk = (TPIE_OS_SIZE_T)b->lk[b->lk.capacity()-1]; + b->lk[b->lk.capacity()-1] = 0; + + DBG("L"); + sc->stream = new POINT_STREAM(sc->stream_name); + sc->stream->persist(PERSIST_DELETE); + if (!sc->stream->is_valid()) { + cerr << "AMI_kdtree bulk loading internal error.\n" + << "[invalid stream restored from file]\n"; + cerr << "Skipping.\n"; + delete sc->stream; + sc->stream = NULL; + continue; + } + delete [] sc->stream_name; + sc->stream_name = NULL; + + if (!can_do_mm(sc->stream->stream_len())) { + cerr << "Temp stream too big: " + << sc->stream->stream_len() << " items.\n"; + cerr << "Aborting.\n"; + exit(1); + } + copy_to_mm(sc->stream, streams_mm, sz); + delete sc->stream; + sc->stream = NULL; + + DBG("B" << (TPIE_OS_OFFSET)sz); + // Build the subtree. + + if (sz <= params_.leaf_size_max) { + + if (sc->low) + b->el[sc->ctx.i].set_low_child(next_free_lk++, BLOCK_LEAF); + else + b->el[sc->ctx.i].set_high_child(next_free_lk++, BLOCK_LEAF); + TPLOG(" b("<bid()<<")->el["<ctx.i<<"].?: ("<lk[next_free_lk - 1], (sc->ctx.d + 1) % dim, streams_mm, sz); + + } else if ((sc->ctx.h + 1 >= max_intranode_height(b->bid())) || + (next_free_el >= params_.node_size_max)) { + + if (sc->low) + b->el[sc->ctx.i].set_low_child(next_free_lk++, BLOCK_NODE); + else + b->el[sc->ctx.i].set_high_child(next_free_lk++, BLOCK_NODE); + TPLOG(" b("<bid()<<")->el["<ctx.i<<"].?: ("<lk[next_free_lk - 1], (sc->ctx.d + 1) % dim, streams_mm, sz); + + } else { + + if (sc->low) + b->el[sc->ctx.i].set_low_child(next_free_el++, BIN_NODE); + else + b->el[sc->ctx.i].set_high_child(next_free_el++, BIN_NODE); + TPLOG(" b("<bid()<<")->el["<ctx.i<<"].?: ("<ctx.h + 1, + (sc->ctx.d + 1) % dim), + streams_mm, sz, next_free_el, next_free_lk); + } + + // save the next_free_* info. + if (b->lk[b->lk.capacity()-1] == 0) { + b->size() = next_free_el; + b->lk[b->lk.capacity()-1] = next_free_lk; + } + + release_node(b); + + DBG(" "); + } +} + +//// *AMI_kdtree::distribute_s* //// +template +void AMI_KDTREE::distribute_s(AMI_bid bid, TPIE_OS_SIZE_T d, sample* s) { + + AMI_KDTREE_NODE* n, *r = fetch_node(bid); + AMI_bid nbid; + POINT* p; + pair a; + int j; + TPIE_OS_OFFSET sz; + + // Create all streams. Could we run out of memory here? + for (j = 0; j < s->q.size(); j++) { + s->q[j].stream = new POINT_STREAM; + s->q[j].stream->name(&s->q[j].stream_name); + s->q[j].stream->persist(PERSIST_PERSISTENT); + } + + s->in_stream->seek(0); + j = 0; + sz = s->in_stream->stream_len(); + while ((s->in_stream->read_item(&p)) == AMI_ERROR_NO_ERROR) { + if (j % 200000 == 0) + DBG("\b\b\b"<find_index(*p); + if (a.second == BLOCK_NODE) + nbid = r->lk[a.first]; + while (a.second == BLOCK_NODE) { + n = fetch_node(nbid); + a = n->find_index(*p); + if (a.second == BLOCK_NODE) + nbid = n->lk[a.first]; + release_node(n); + } + + assert(a.second == GRID_INDEX); + assert(a.first < s->q.size()); + + s->q[a.first].stream->write_item(*p); + } + + DBG("\b\b\b \b\b\b"); + release_node(r); + + // Delete output streams. We'll read them later, one by one. + for (j = 0; j < s->q.size(); j++) { + delete s->q[j].stream; + s->q[j].stream = NULL; + } +} + + + +#undef AMI_KDTREE_LEAF +#undef AMI_KDTREE_NODE +#undef AMI_KDTREE +#undef POINT +#undef POINT_STREAM + +#endif //_AMI_KDTREE_H diff --git a/fastlib/u/nvasil/tpie/ami_key.cc b/fastlib/u/nvasil/tpie/ami_key.cc new file mode 100644 index 0000000000..6638cc225a --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_key.cc @@ -0,0 +1,23 @@ +// Copyright (c) 1995 Darren Erik Vengroff +// +// File: ami_key.cpp +// Author: Darren Erik Vengroff +// 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; +} + diff --git a/fastlib/u/nvasil/tpie/ami_key.h b/fastlib/u/nvasil/tpie/ami_key.h new file mode 100644 index 0000000000..799ac08917 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_key.h @@ -0,0 +1,36 @@ +// Copyright (c) 1995 Darren Erik Vengroff +// +// File: ami_key.h +// Author: Darren Erik Vengroff +// 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 + +// 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 diff --git a/fastlib/u/nvasil/tpie/ami_logmethod.h b/fastlib/u/nvasil/tpie/ami_logmethod.h new file mode 100644 index 0000000000..454b6899e8 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_logmethod.h @@ -0,0 +1,545 @@ +// Copyright (c) 2001 Octavian Procopiuc +// +// File: ami_logmethod.h +// Author: Octavian Procopiuc +// +// $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 + +// For vector +#include +// For pair +#include +// TPIE stuff. +#include +#include + +#include + +#define LM_PATH_NAME_LENGTH 128 + +template +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*); +// void persist(persistence); +// void unload(AMI_STREAM*); +// +// Requirements specific to T: +// const Tp& params(); +// void load(AMI_STREAM*); +// 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 Logmethod_base { +public: + typedef AMI_STREAM stream_t; + typedef Logmethod_params 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 &mbr(); + // Inquire the size. + TPIE_OS_OFFSET size() const { return header_.size; } + // Inquire the run-time parameters. + const Logmethod_params& 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& 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 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 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 Logmethod2: public Logmethod_base { + protected: + using Logmethod_base::tree0_; + using Logmethod_base::trees_; + using Logmethod_base::params_; + using Logmethod_base::stats_; + using Logmethod_base::header_; + + public: + using Logmethod_base::create_tree; + + Logmethod2(const char *base_file_name, const Logmethod_params ¶ms); + bool insert(const Value& p); +}; + + +template +class LogmethodB: public Logmethod_base { + protected: + using Logmethod_base::tree0_; + using Logmethod_base::trees_; + using Logmethod_base::params_; + using Logmethod_base::stats_; + using Logmethod_base::header_; + + public: + using Logmethod_base::create_tree; + + LogmethodB(const char *base_file_name, const Logmethod_params ¶ms); + bool insert(const Value& p); + static size_t B; +}; + + +//////////////////////////////////////////// +/////////// ***Implementation*** /////////// +//////////////////////////////////////////// + +#define LOGMETHOD_BASE Logmethod_base +#define LOGMETHOD2 Logmethod2 +#define LOGMETHODB LogmethodB + + +/////////////////////////////////////// +///////// **Logmethod_base** ////////// +/////////////////////////////////////// + +//// *Logmethod_base::Logmethod_base* //// +template +LOGMETHOD_BASE::Logmethod_base(const char *base_file_name, + const Logmethod_params ¶ms): + 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 +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 +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 +TPIE_OS_OFFSET LOGMETHOD_BASE::window_query(const Key &lop, const Key &hip, + AMI_STREAM* 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 +void LOGMETHOD_BASE::persist(persistence per) { + per_ = per; +} + +//// *Logmethod_base::~Logmethod_base* //// +template +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 +const pair& 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 +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 +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 +LOGMETHOD2::Logmethod2(const char* base_file_name, + const Logmethod_params ¶ms): + Logmethod_base(base_file_name, params) { +} + +//// *Logmethod2::insert* //// +template +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 +LOGMETHODB::LogmethodB(const char* base_file_name, + const Logmethod_params ¶ms): + Logmethod_base(base_file_name, params) { +} + +//// *LogmethodB::insert* //// +template +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 +size_t LOGMETHODB::B = 100; + +#endif // _LOGMETHOD_H diff --git a/fastlib/u/nvasil/tpie/ami_matrix.h b/fastlib/u/nvasil/tpie/ami_matrix.h new file mode 100644 index 0000000000..713b5229a0 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_matrix.h @@ -0,0 +1,519 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: ami_matrix.h +// Author: Darren Vengroff +// 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 + +//#define QUICK_MATRIX_MULT 1 +#define AGGARWAL_MATRIX_MULT 1 + +#define INTERNAL_TIMING 1 + +#ifdef INTERNAL_TIMING +# include +# include +#endif + +#include + +#include +#include +#include + +#include + +template +class AMI_matrix : public AMI_STREAM { +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 +AMI_matrix::AMI_matrix(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col) : + r(row), c(col), AMI_STREAM() +{ +} + +template +AMI_matrix::~AMI_matrix(void) +{ +} + +template +TPIE_OS_OFFSET AMI_matrix::rows(void) +{ + return r; +} + +template +TPIE_OS_OFFSET AMI_matrix::cols(void) +{ + return c; +} + +// Add two matrices. + +template +AMI_err AMI_matrix_add(AMI_matrix &op1, AMI_matrix &op2, + AMI_matrix &res) +{ + AMI_scan_add sa; + + // We should do some bound checking here. + + return AMI_scan((AMI_STREAM *)&op1, (AMI_STREAM *)&op2, + &sa, (AMI_STREAM *)&res); +} + +// Subtract. + +template +AMI_err AMI_matrix_sub(AMI_matrix &op1, AMI_matrix &op2, + AMI_matrix &res) +{ + AMI_scan_sub ss; + + // We should do some bound checking here. + + return AMI_scan((AMI_STREAM *)&op1, (AMI_STREAM *)&op2, + &ss, (AMI_STREAM *)&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 +AMI_err AMI_matrix_mult(AMI_matrix &op1, AMI_matrix &op2, + AMI_matrix &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)) { + + TPIE_OS_SIZE_T ii,jj; + T *tmp_read; + + // Main memory copies of the matrices. + matrix mm_op1((TPIE_OS_SIZE_T)op1.rows(), (TPIE_OS_SIZE_T)op1.cols()); + matrix mm_op2((TPIE_OS_SIZE_T)op2.rows(), (TPIE_OS_SIZE_T)op2.cols()); + matrix 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 &)mm_op1, + (matrix_base &)mm_op2, + (matrix_base &)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 *op1p = new AMI_matrix(rowsp1, colsp1); + AMI_matrix *op2p = new AMI_matrix(rowsp2, colsp2); + + // Scan each matrix to pad it out with zeroes as needed. + + { + AMI_matrix_pad smp1(op1.rows(), op1.cols(), mm_matrix_extent); + AMI_matrix_pad smp2(op2.rows(), op2.cols(), mm_matrix_extent); + + ae = AMI_scan((AMI_STREAM *)&op1, &smp1, + (AMI_STREAM *)op1p); + if (ae != AMI_ERROR_NO_ERROR) { + return ae; + } + ae = AMI_scan((AMI_STREAM *)&op2, &smp2, + (AMI_STREAM *)op2p); + if (ae != AMI_ERROR_NO_ERROR) { + return ae; + } + } + + // Permuted padded matrices. + + AMI_matrix *op1pp = new AMI_matrix(rowsp1, colsp1); + AMI_matrix *op2pp = new AMI_matrix(rowsp2, colsp2); + + AMI_matrix *respp = new AMI_matrix(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 *)op1p, + (AMI_STREAM *)op1pp, + (AMI_gen_perm_object *)&pmib1); + if (ae != AMI_ERROR_NO_ERROR) { + return ae; + } + + ae = AMI_general_permute((AMI_STREAM *)op2p, + (AMI_STREAM *)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 mm_op1(mm_matrix_extent, mm_matrix_extent); + matrix mm_op2(mm_matrix_extent, mm_matrix_extent); + matrix 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 &)mm_op1, + (matrix_base &)mm_op2, + (matrix_base &)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 *resp = new AMI_matrix(rowsp1, colsp2); + + { + perm_matrix_outof_blocks pmob(rowsp1, colsp1, mm_matrix_extent); + + ae = AMI_general_permute((AMI_STREAM *)respp, + (AMI_STREAM *)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 smup(op1.rows(), op2.cols(), + mm_matrix_extent); + + ae = AMI_scan((AMI_STREAM *)resp, &smup, + (AMI_STREAM *)&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 diff --git a/fastlib/u/nvasil/tpie/ami_matrix_blocks.cc b/fastlib/u/nvasil/tpie/ami_matrix_blocks.cc new file mode 100644 index 0000000000..15dcf387a4 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_matrix_blocks.cc @@ -0,0 +1,134 @@ +// +// File: ami_matrix_blocks.cpp +// Author: Darren Vengroff +// Created: 12/11/94 +// + +#include +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 + +#include +#include +#include + +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( (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( (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; +} + diff --git a/fastlib/u/nvasil/tpie/ami_matrix_blocks.h b/fastlib/u/nvasil/tpie/ami_matrix_blocks.h new file mode 100644 index 0000000000..a2e8ef5e5b --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_matrix_blocks.h @@ -0,0 +1,43 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: ami_matrix_blocks.h +// Author: Darren Vengroff +// 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 +// Get AMI_gen_perm_object. +#include + +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 + + + diff --git a/fastlib/u/nvasil/tpie/ami_matrix_fill.h b/fastlib/u/nvasil/tpie/ami_matrix_fill.h new file mode 100644 index 0000000000..36a3a09f0d --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_matrix_fill.h @@ -0,0 +1,71 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: ami_matrix_fill.h +// Author: Darren Vengroff +// 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 +// Get the AMI_scan_object definition. +#include + +template +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 AMI_matrix_fill_scan : AMI_scan_object { +private: + TPIE_OS_OFFSET r, c; + TPIE_OS_OFFSET cur_row, cur_col; + AMI_matrix_filler *pemf; +public: + AMI_matrix_fill_scan(AMI_matrix_filler *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 +AMI_err AMI_matrix_fill(AMI_matrix *pem, AMI_matrix_filler *pemf) +{ + AMI_err ae; + + ae = pemf->initialize(pem->rows(), pem->cols()); + if (ae != AMI_ERROR_NO_ERROR) { + return ae; + } + + AMI_matrix_fill_scan emfs(pemf, pem->rows(), pem->cols()); + + return AMI_scan(&emfs, (AMI_STREAM *)pem); +}; + +#endif // _AMI_MATRIX_FILL_H diff --git a/fastlib/u/nvasil/tpie/ami_matrix_pad.h b/fastlib/u/nvasil/tpie/ami_matrix_pad.h new file mode 100644 index 0000000000..d3d4837b44 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_matrix_pad.h @@ -0,0 +1,174 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: ami_matrix_pad.h +// Author: Darren Vengroff +// 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 +// Get definition of AMI_scan_object class. +#include + +// 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 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 +AMI_matrix_pad::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 +AMI_matrix_pad::~AMI_matrix_pad() +{ +} + +template +AMI_err AMI_matrix_pad::initialize(void) +{ + cur_col = cur_row = 0; + return AMI_ERROR_NO_ERROR; +} + +template +AMI_err AMI_matrix_pad::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 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 +AMI_matrix_unpad::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 +AMI_matrix_unpad::~AMI_matrix_unpad() +{ +} + +template +AMI_err AMI_matrix_unpad::initialize(void) +{ + cur_col = cur_row = 0; + return AMI_ERROR_NO_ERROR; +} + +template +AMI_err AMI_matrix_unpad::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 diff --git a/fastlib/u/nvasil/tpie/ami_merge.h b/fastlib/u/nvasil/tpie/ami_merge.h new file mode 100644 index 0000000000..9aadf56d82 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_merge.h @@ -0,0 +1,813 @@ +// +// File: ami_merge.h +// Author: Darren Erik Vengroff +// 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 + +// For log() and such as needed to compute tree heights. +#include + +#include + +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 AMI_generalized_merge_base; + + +//merge streams using a merge management object and write +//result into ; it is assumed that the available memory can +//fit the 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 +AMI_err AMI_generalized_merge(AMI_STREAM **instreams, arity_t arity, + AMI_STREAM *outstream, M *m_obj); + + +// divide the input stream in substreams, merge each substream +// recursively, and merge them together using AMI_generalized_single_merge() +template +AMI_err AMI_generalized_partition_and_merge(AMI_STREAM *instream, + AMI_STREAM *outstream, M *m_obj); + + +//merge streams in memory using a merge management object and +//write result into ; +template +AMI_err AMI_generalized_single_merge(AMI_STREAM **instreams, arity_t arity, + AMI_STREAM *outstream, M *m_obj); + + +//read in memory and merge it using +//m_obj->main_mem_operate(); if does not fit in main memory +//return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; +template +AMI_err AMI_main_mem_merge(AMI_STREAM *instream, + AMI_STREAM *outstream, M *m_obj); + +//------------------------------------------------------------ + + + + + + + +//------------------------------------------------------------ +// A superclass for merge management objects +//------------------------------------------------------------ +template +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 streams using a merge management object and write +//result into ; it is assumed that the available memory can +//fit the 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 +AMI_err +AMI_generalized_merge(AMI_STREAM **instreams, arity_t arity, + AMI_STREAM *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 streams in memory using a merge management object and +//write result into ; + +//------------------------------------------------------------ +template +AMI_err +AMI_generalized_single_merge(AMI_STREAM **instreams, arity_t arity, + AMI_STREAM *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 in memory and merge it using +//m_obj->main_mem_operate(); if does not fit in main memory +//return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + +//------------------------------------------------------------ +template +AMI_err AMI_main_mem_merge(AMI_STREAM *instream, + AMI_STREAM *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 +AMI_err AMI_generalized_partition_and_merge(AMI_STREAM *instream, + AMI_STREAM *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 *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 *current_input; + + // The output stream for the current level if it is not outstream. + AMI_STREAM *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; + 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* *the_substreams = new AMI_STREAM*[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 **) + (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; + + // 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 **) + (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 diff --git a/fastlib/u/nvasil/tpie/ami_optimized_merge.h b/fastlib/u/nvasil/tpie/ami_optimized_merge.h new file mode 100644 index 0000000000..f403049ba2 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_optimized_merge.h @@ -0,0 +1,3348 @@ +// +// File: ami_optimized_merge.h +// Author: Rakesh Barve +// +//cleaned up: laura (tried to..) TO DO: the 3 polymorphs of +//AMI_partition_and_merge() have each 1100 lines of code and are +//almost identical; similarly, the 3 polymorphs of AMI_single_merge() +//differ in one line.. +// +// TO DO: (jan) Check whether all new's are matched by corresponding +// delete's (especially before prematurely "return"ing). +// +// Function: AMI_partition_and_merge() was modified from Darren's +// original version, so as to ensure "sequential access." The +// function AMI_single_merge(), which uses a merge management object +// and a priority queue class to carry out internal memory merging +// computation, now has a "pure C" alternative that seems to perform +// better by a huge margin: This function is called AMI_single_merge() +// (a polymorph, without merge management object) and is based on a +// simple heap data structure straight out of CLR (Introduction to +// ALgorithms) in mergeheap.h There is also a merge using +// replacement selection based run formation. There is also a +// provision to use a run formation that uses a quicksort using only +// keys of the items; there is a provision to to use templated heaps +// to implement the merge. + +// $Id: ami_optimized_merge.h,v 1.59 2005/07/07 20:43:49 adanner Exp $ + +// TO DO: substream_count setting; don't depend on current_stream_len + +//COMMENT REGARDING BTE_IMP_USER_DEFINED: USER_DEFINED is what is +//currently the name for STRIPED_BTE. As of now, STRIPED_BTE is not +//part of TPIE distribution. Once it becomes partof TPIE distribution +//the BTE_IMP_USER_DEFINED flag will begin to be used. + +#ifndef _AMI_OPTIMIZED_MERGE_H +#define _AMI_OPTIMIZED_MERGE_H + +// Get definitions for working with Unix and Windows +#include + +// For log() and such as needed to compute tree heights. +#include + +#include +#include + +#include +#include //For templated heaps +#include //For templated qsort_items +#include + +typedef int AMI_merge_flag; +typedef int arity_t; + +//enable debugging messages in AMI_partition_and_merge(..) +// #define XXX TP_LOG_DEBUG_ID("AMI_partition_and_merge_stream"); +#define XXX + +//------------------------------------------------------------ +// FUNCTIONS DEFINED IN THIS MODULE +//------------------------------------------------------------ + +//These are polymorphs of AMI_single_merge in ami_merge.h; merge input +//streams using a 'hardwired' heap, without using a merge-management +//object, but: + +//using < operator +template < class T > +AMI_err AMI_single_merge (AMI_STREAM < T > **, arity_t, + AMI_STREAM < T > *); + + +// Comment: (jan) Do not use this version anymore + +// //do not use <, use specified comparison function + +// End Comment. + + +//make use of the explicit knowledge of the key of the user-defined +//records +template < class T, class KEY > +AMI_err AMI_single_merge (AMI_STREAM < T > **, arity_t, + AMI_STREAM < T > *, int, KEY); + +//These are polymorphs of AMI_merge in ami_merge.h, each corresponding +//to one of AMI_single_merge's polymorphs defined above; merge +//streams using a merge management object and write result into +//; it is assumed that the available memory can fit the +// streams, the output stream and also the space required by +//the merge management object; + +template < class T > +AMI_err AMI_merge (AMI_STREAM < T > **, arity_t, AMI_STREAM < T > *); + + +template < class T, class KEY > +AMI_err AMI_merge (AMI_STREAM < T > **, arity_t, + AMI_STREAM < T > *, int, KEY); + +//These are polymorphs of AMI_partition_and_merge in +//ami_merge.h;divide the input stream in substreams, merge each +//substream recursively, and merge them together using one of +//AMI_single_merge() polymorphs defined above; + +template < class T > +AMI_err AMI_partition_and_merge (AMI_STREAM < T > *instream, + AMI_STREAM < T > *outstream); + +template < class T, class KEY > +AMI_err AMI_partition_and_merge (AMI_STREAM < T > *instream, + AMI_STREAM < T > *outstream, + int keyoffset, KEY dummykey); + +//------------------------------------------------------------ +//static classes functions + +//class describing a run formation item +//static template class run_formation_item; + +template < class T > +static size_t +count_stream_overhead (AMI_STREAM < T > **instreams, arity_t arity); + +template < class T, class KEY > +static AMI_err +Run_Formation_Algo_R_Key (AMI_STREAM < T > *, arity_t, AMI_STREAM < T > **, + char *, size_t, int *, int **, int, int, int, + KEY); + +template < class T, class KEY > +static AMI_err +AMI_replacement_selection_and_merge_Key (AMI_STREAM < T > *instream, + AMI_STREAM < T > *outstream, + int keyoffset, KEY dummykey); + +static inline void +stream_name_generator (char *prepre, char *pre, int id, char *dest); +//------------------------------------------------------------ + +//------------------------------------------------------------ +//class describing a run formation item +template < class KEY > class run_formation_item { +public: + KEY Key; + unsigned int RecordPtr; + unsigned int Loser; + short RunNumber; + unsigned int ParentExt; + unsigned int ParentInt; + +public: + friend int operator == (const run_formation_item & x, + const run_formation_item & y) + { return (x.Key == y.Key);}; + + friend int operator != (const run_formation_item & x, + const run_formation_item & y) { + return (x.Key != y.Key); + }; + + friend int operator <= (const run_formation_item & x, + const run_formation_item & y) { + return (x.Key <= y.Key); + }; + + friend int operator >= (const run_formation_item & x, + const run_formation_item & y) { + return (x.Key >= y.Key); + }; + + friend int operator < (const run_formation_item & x, + const run_formation_item & y) { + return (x.Key < y.Key); + }; + + friend int operator > (const run_formation_item & x, + const run_formation_item & y) { + return (x.Key > y.Key); + }; + +}; + +//------------------------------------------------------------ +//This is polymorph to AMI_single_merge in ami_merge.h; merge input +//streams using a 'hardwired' heap, without using a merge-management +//object +//------------------------------------------------------------ +template < class T > +AMI_err AMI_single_merge (AMI_STREAM < T > **instreams, arity_t arity, + AMI_STREAM < T > *outstream) +{ + unsigned int i, j; + AMI_err ami_err; + T merge_out; + + //the mergeheap + class merge_heap_element < T > *K_Array = + new merge_heap_element[arity + 1]; + + //Pointers to current leading elements of streams + T* *in_objects = new T*[arity + 1]; + + //The number of actual heap elements at any time: can change even + //after the merge begins because whenever some stream gets + //completely depleted, heapsize decremnents by one. + int heapsize_H; + + // Rewind and read the first item from every stream. + j = 1; + for (i = 0; i < arity; i++) { + + if ((ami_err = instreams[i]->seek (0)) != AMI_ERROR_NO_ERROR) { + delete[] in_objects; + return ami_err; + } + 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 { + //read_item succesful: Set the taken flags to 0 before we call + //intialize() + K_Array[j].key = *in_objects[i]; + K_Array[j].run_id = i; + j++; + } + } + + //build a heap from the smallest items of each stream + unsigned int NonEmptyRuns = j - 1; + + merge_heap < T > Main_Merge_Heap (K_Array, NonEmptyRuns); + + while (Main_Merge_Heap.sizeofheap ()) { + i = Main_Merge_Heap.get_min_run_id (); + if ((ami_err = outstream->write_item (*in_objects[i])) + != AMI_ERROR_NO_ERROR) { + delete[] in_objects; + return ami_err; + } + 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) { + Main_Merge_Heap.delete_min_and_insert ((T *) NULL); + } else { + Main_Merge_Heap.delete_min_and_insert (in_objects[i]); + } + } //while + + return AMI_ERROR_NO_ERROR; +} + + +//------------------------------------------------------------ +//This is a polymorph of AMI_single_merge in ami_merge.h; merge input +//streams using a 'hardwired' heap, without using a merge-management +//object; it makes use of the explicit knowledge of the key of the +//user-defined records +//------------------------------------------------------------ +template < class T, class KEY > +AMI_err +AMI_single_merge (AMI_STREAM < T > **instreams, arity_t arity, + AMI_STREAM < T > *outstream, int keyoffset, KEY dummykey) +{ + unsigned int i, j; + AMI_err ami_err; + T merge_out; + +/* //The number of actual heap elements at any time: can change even + //after the merge begins because whenever some stream gets completely + //depleted, heapsize decremnents by one. + int heapsize_H; +*/ + //the mergeheap + class merge_heap_element < KEY > *K_Array = + new merge_heap_element[arity + 1]; + + //Pointers to current leading elements of streams + T* *in_objects = new T*[arity + 1]; + + // Rewind and read the first item from every stream. + j = 1; + for (i = 0; i < (int) arity; i++) { + + if ((ami_err = instreams[i]->seek (0)) != AMI_ERROR_NO_ERROR) { + delete[] in_objects; + return ami_err; + } + 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 { + // Set the taken flags to 0 before we call intialize() + K_Array[j].key = *((KEY*)((char *) in_objects[i] + keyoffset)); + K_Array[j].run_id = i; + j++; + } + } + + //build a heap from the smallest items of each stream + unsigned int NonEmptyRuns = j - 1; + + merge_heap < KEY > Main_Merge_Heap (K_Array, NonEmptyRuns); + + while (Main_Merge_Heap.sizeofheap ()) { + + i = Main_Merge_Heap.get_min_run_id (); + if ((ami_err = outstream->write_item (*in_objects[i])) + != AMI_ERROR_NO_ERROR) { + delete[] in_objects; + return ami_err; + } + 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) { + Main_Merge_Heap.delete_min_and_insert ((KEY *) NULL); + } else { + Main_Merge_Heap.delete_min_and_insert + ((KEY *) ((char *) in_objects[i] + keyoffset)); + } + } //while + + return AMI_ERROR_NO_ERROR; +} + +//------------------------------------------------------------ +//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 +//------------------------------------------------------------ +template < class T > +size_t +count_stream_overhead (AMI_STREAM < T > **instreams, arity_t arity) +{ + size_t sz_stream, sz_needed = 0; + + 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; + } + return sz_needed; +} + +//------------------------------------------------------------ +//These are polymorphs of AMI_merge in ami_merge.h, each corresponding +//to one of AMI_single_merge's polymorphs defined above; merge +//streams using a merge management object and write result into +//; it is assumed that the available memory can fit the +// streams, the output stream and also the space required by +//the merge management object; + +//------------------------------------------------------------ +template < class T > +AMI_err +AMI_merge (AMI_STREAM < T > **instreams, arity_t arity, + AMI_STREAM < T > *outstream) +{ + size_t sz_avail; + size_t sz_needed; + + // How much main memory is available? + sz_avail = MM_manager.memory_available (); + + //make sure all streams fit in available memory + sz_needed = count_stream_overhead (instreams, arity); + if (sz_needed >= sz_avail) { + TP_LOG_FATAL_ID ("Insufficent main memory to perform a merge."); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + // assert (sz_needed < sz_avail); just checked this.. dh + + //should count the space overhead used by merge..merge should + //implement a function which returns it; for the moment just rely on + //the merge routine that it returns an error + //(AMI_ERROR_INSUFFICIENT_MEMORY) if there is n ot enough memory; + + return AMI_single_merge (instreams, arity, outstream); +} + + +//------------------------------------------------------------ +template < class T, class KEY > +AMI_err +AMI_merge (AMI_STREAM < T > **instreams, arity_t arity, + AMI_STREAM < T > *outstream, int keyoffset, KEY dummy) +{ + size_t sz_avail; + size_t sz_needed; + + // How much main memory is available? + sz_avail = MM_manager.memory_available (); + + //make sure all streams fit in available memory + sz_needed = count_stream_overhead (instreams, arity); + if (sz_needed >= sz_avail) { + TP_LOG_FATAL_ID ("Insuficent main memory to perform a merge."); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + // assert (sz_needed < sz_avail); just checked this .. dh + + //should count the space overhead used by merge..merge should + //implement a function which returns it; for the moment just rely on + //the merge routine that it returns an error + //(AMI_ERROR_INSUFFICIENT_MEMORY) if there is n ot enough memory; + + return AMI_single_merge (instreams, arity, keyoffset, dummy); +} + +//------------------------------------------------------------ +static inline void +stream_name_generator (char *prepre, char *pre, int id, char *dest) +{ + char tmparray[5]; + + strcpy (dest, prepre); + strcat (dest, pre); + sprintf (tmparray, "%d", id); + strcat (dest, tmparray); +} + +//------------------------------------------------------------ +//This is a polymorph of AMI_partition_and_merge in ami_merge.h;divide +//the input stream in substreams, merge each substream recursively, +//and merge them together using AMI_single_merge(AMI_STREAM **, +//arity_t , AMI_STREAM *); +//------------------------------------------------------------ +template < class T > +AMI_err +AMI_partition_and_merge (AMI_STREAM < T > *instream, + AMI_STREAM < T > *outstream) +{ + AMI_err ae; + TPIE_OS_OFFSET len; + size_t sz_avail, sz_stream; + size_t sz_substream; + + unsigned int ii, jj, kk; + int ii_streams; + + char *working_disk; + + TP_LOG_DEBUG_ID ("AMI_partition_and_merge_stream START"); + + // Figure out how much memory we've got to work with. + + sz_avail = MM_manager.memory_available (); + + //Conservatively assume that the memory for buffers for + //the two streams is unallocated; so we need to subtract. + if ((ae = instream->main_memory_usage (&sz_stream, + MM_STREAM_USAGE_MAXIMUM)) != + AMI_ERROR_NO_ERROR) { + TP_LOG_DEBUG_ID ("memory error"); + return ae; + } + + if ((ae = instream->main_memory_usage (&sz_substream, + MM_STREAM_USAGE_OVERHEAD)) != + AMI_ERROR_NO_ERROR) { + + TP_LOG_DEBUG_ID ("memory error"); + return ae; + } + sz_avail -= 2 * sz_stream; + + working_disk = tpie_tempnam("AMI"); + + // 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 (); + instream->seek (0); + + if ((len * sizeof (T)) <= sz_avail) { + + T *next_item; + T *mm_stream = new T[len]; + + for (int i = 0; i < len; i++) { + if ((ae = instream->read_item (&next_item)) != AMI_ERROR_NO_ERROR) { + TP_LOG_DEBUG_ID ("read error"); + return ae; + } + mm_stream[i] = *next_item; + } + quick_sort_op ((T *) mm_stream, len); + + for (int i = 0; i < len; i++) { + if ((ae = outstream->write_item (mm_stream[i])) + != AMI_ERROR_NO_ERROR) { + TP_LOG_DEBUG_ID ("write error"); + if (mm_stream) + delete[] mm_stream; + return ae; + } + } + + if (mm_stream) { + delete[] mm_stream; + mm_stream = NULL; + } + + return AMI_ERROR_NO_ERROR; + + } else { + + // The number of substreams that the original input stream + // will be split into. + arity_t original_substreams; + + // The length, in terms of stream objects of type T, of the + // original substreams of the input stream. The last one may + // be shorter than this. + + size_t sz_original_substream; + + // The initial temporary stream, to which substreams of the + // original input stream are written. + + //RAKESH + AMI_STREAM < T > **initial_tmp_stream; + + // The number of substreams that can be merged together at once. + + arity_t merge_arity; + + // 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. + + //RAKESH + AMI_STREAM < T > **current_input; + + // The output stream for the current level if it is not outstream. + + //RAKESH + AMI_STREAM < T > **intermediate_tmp_stream; + + //RAKESH FIX THIS: Need to generate random strings using + //tmpname() or something like that. + char *prefix_name[] = { "_0_", "_1_" }; + char itoa_str[5]; + + // The size of substreams of *current_input that are being + // merged. The last one may be smaller. This value should be + // sz_original_substream * (merge_arity ** k) where k is the + // number of iterations the loop has gone through. + + //Merge Level + 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. + +//RAKESH +// In our case merge_arity is determined differently than in the original +// implementation of AMI_partition_and_merge since we use several streams +// in each level. +// In our case net main memory required to carry out an R-way merge is +// (R+1)*MM_STREAM_USAGE_MAXIMUM {R substreams for input runs, 1 stream for output} +// + R*MM_STREAM_USAGE_OVERHEAD {One stream for each active input run: but while +// the substreams use buffers, streams don't} +// + (R+1)*m_obj->space_usage_per_stream(); +// +// The net memory usage for an R-way merge is thus +// R*(sz_stream + sz_substeam + m_obj->space_usage_per_stream()) + sz_stream + +// m_obj->space_usage_per_stream(); +// + + //To support a binary merge, need space for max_stream_usage + //for at least three stream objects. + + if (sz_avail <= 3 * (sz_stream + sz_substream + + sizeof (merge_heap_element < T >)) ) { + TP_LOG_FATAL_ID + ("Insufficient Memory for AMI_partition_and_merge_stream()"); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + + sz_original_substream = (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. + + { + size_t sz_chunk_size = instream->chunk_size (); + + sz_original_substream = sz_chunk_size * + ((sz_original_substream + sz_chunk_size - 1) / sz_chunk_size); + } + + original_substreams = (len + sz_original_substream - 1) / + sz_original_substream; + + // Account for the space that a merge object will use. + + { + //Availabe memory for input stream objects is given by + //sz_avail minus the space occupied by output stream objects. + size_t sz_avail_during_merge = sz_avail - + + sz_stream - sz_substream; + + //This conts the per-input stream memory cost. + size_t sz_stream_during_merge = sz_stream + sz_substream + + sizeof (merge_heap_element < T >); + + //Compute merge arity + merge_arity = sz_avail_during_merge / 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 <= 5) { + TP_LOG_FATAL_ID ("out of streams"); + 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_partition_and_merge(): merge arity = " << + merge_arity ); + + if (merge_arity < 2) { + + TP_LOG_FATAL_ID + ("Insufficient memory for AMI_partition_and_merge_stream()"); + + 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) original_substreams) / + 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 >= original_substreams, + "Number of permitted substreams was reduced."); + + // How big will such substreams be? + + double new_sz_original_substream = ceil ((double) max len_original_substreams); + + tp_assert (new_sz_original_substream <= sz_original_substream, + "Size of original streams increased."); + + sz_original_substream = (size_t) new_sz_original_substream; + + TP_LOG_DEBUG_ID ("Memory constraints set original substreams = " << + original_substreams << '\n'); + + original_substreams = (len + sz_original_substream - 1) / + sz_original_substream; + + TP_LOG_DEBUG_ID ("Tree height constraints set original substreams = " + << original_substreams << '\n'); + } + +#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. + + // Comment: (jan) Use VarArray for ANSI-compliance. + + // unsigned int run_lengths[2][merge_arity] + // [ (original_substreams + merge_arity - 1) / merge_arity]; + // int Sub_Start[merge_arity]; + + // End Comment. + + VarArray3D + run_lengths(2, merge_arity, + (original_substreams + merge_arity - 1) / merge_arity); + + VarArray1D Sub_Start(merge_arity); + + // Comment: (jan) initialization is done by the VarArray constructor. + + // memset ((void *) run_lengths, 0, + // 2 * merge_arity * ((original_substreams + merge_arity - 1) / + // merge_arity) * sizeof (unsigned int)); + + // End Comment. + + initial_tmp_stream = new AMI_STREAM *[merge_arity]; + mm_stream = new T[sz_original_substream]; + + tp_assert (mm_stream != NULL, "Misjudged available main memory."); + + if (mm_stream == NULL) { + TP_LOG_FATAL_ID ("internal error"); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + + instream->seek (0); + + tp_assert (original_substreams * sz_original_substream - len < + sz_original_substream, + "Total substream length too long or too many."); + + tp_assert (len - (original_substreams - 1) * sz_original_substream <= + sz_original_substream, + "Total substream length too short or too few."); + +//RAKESH + size_t check_size = 0; + int current_stream = merge_arity - 1; + + int runs_in_current_stream = 0; + int *desired_runs_in_stream = new int[merge_arity]; + char new_stream_name[BTE_STREAM_PATH_NAME_LEN]; + + //For the first stream: + for (ii_streams = 0; ii_streams < merge_arity; ii_streams++) { + + //Figure out how many runs go in each one of merge_arity streams? + // If there are 12 runs to be distributed among 5 streams, the first + //three get 2 and the last two get 3 runs + + if (ii_streams < + (merge_arity - + (original_substreams % + merge_arity))) desired_runs_in_stream[ii_streams] = + original_substreams / merge_arity; + + else + desired_runs_in_stream[ii_streams] = + (original_substreams + merge_arity - 1) / merge_arity; + } + +#ifndef BTE_IMP_USER_DEFINED + +// new_name_from_prefix(prefix_name[0],current_stream, new_stream_name); + + //The assumption here is that working_disk is the name of the specific + //directory in which the temporary/intermediate streams will be made. + //By default, I think we shd + + stream_name_generator (working_disk, + prefix_name[0], + current_stream, new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[0], + current_stream, new_stream_name); + +#endif + + initial_tmp_stream[current_stream] = + new AMI_STREAM < T > (new_stream_name); + initial_tmp_stream[current_stream]->persist (PERSIST_PERSISTENT); + + ii = 0; + while (ii < original_substreams) { + TPIE_OS_OFFSET mm_len; + + // Make sure that the current_stream is supposed to get a run + + if (desired_runs_in_stream[current_stream] > + runs_in_current_stream) { + if (ii == original_substreams - 1) { + mm_len = len % sz_original_substream; + + // If it is an exact multiple, then the mod will come + // out 0, which is wrong. + + if (!mm_len) { + mm_len = sz_original_substream; + } + } else { + mm_len = sz_original_substream; + } + +#if DEBUG_ASSERTIONS + TPIE_OS_OFFSET mm_len_bak = mm_len; +#endif + + // Read a memory load out of the input stream one item at a time, + // fill up the key array at the same time. + { + T *next_item; + + for (int i = 0; i < mm_len; i++) { + if ((ae = instream->read_item (&next_item)) != + AMI_ERROR_NO_ERROR) { + TP_LOG_DEBUG_ID ("read error"); + return ae; + } + mm_stream[i] = *next_item; + } + + //Sort the array. + quick_sort_op ((T *) mm_stream, mm_len); + + for (int i = 0; i < mm_len; i++) { + if ( + (ae = + initial_tmp_stream[current_stream]->write_item + (mm_stream[i])) != AMI_ERROR_NO_ERROR) { + TP_LOG_DEBUG_ID ("write error"); + return ae; + } + + } + + run_lengths(0, current_stream, runs_in_current_stream) = + mm_len; + + } + + runs_in_current_stream++; + ii++; + + } +//RAKESH + if (runs_in_current_stream == + desired_runs_in_stream[current_stream]) { + + check_size += + initial_tmp_stream[current_stream]->stream_len (); + + // We do not want old streams hanging around + // occuping memory. We know how to get the streams + // since we can generate their names + if (initial_tmp_stream[current_stream]) { + + delete initial_tmp_stream[current_stream]; + + initial_tmp_stream[current_stream] = NULL; + + } + + if (check_size < instream->stream_len ()) { + + current_stream = (current_stream + merge_arity - 1) + % merge_arity; + +#ifndef BTE_IMP_USER_DEFINED + // new_name_from_prefix(prefix_name[0],current_stream, new_stream_name); + + stream_name_generator (working_disk, + prefix_name[0], + current_stream, new_stream_name); + +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[0], + current_stream, new_stream_name); +#endif + + initial_tmp_stream[current_stream] = + new AMI_STREAM < T > (new_stream_name); + + initial_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + + // Number of runs packed into + // the stream just constructed now + + runs_in_current_stream = 0; + } + } + + } + + if (initial_tmp_stream[current_stream]) { + delete initial_tmp_stream[current_stream]; + + initial_tmp_stream[current_stream] = NULL; + } + + if (mm_stream) { + delete[]mm_stream; + mm_stream = NULL; + } + // 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 () == check_size, + "Stream lengths do not match:" << + "\n\tinstream->stream_len() = " << instream->stream_len () + << "\n\tinitial_tmp_stream->stream_len() = " << check_size + << ".\n"); + + //We now delete the input stream. Note that if instream has + //its persistence member set to PERSIST_DELETE, instream will + //be deleted from disk. + + //delete instream; + + // Set up the loop invariants for the first iteration of hte + // main loop. + + current_input = initial_tmp_stream; + + //Monitoring prints. + + TP_LOG_DEBUG_ID ("Number of runs from run formation is " << + original_substreams ); + TP_LOG_DEBUG_ID ("Merge arity is " << merge_arity ); + + // Pointers to the substreams that will be merged. +//RAKESH + AMI_STREAM < T > **the_substreams = + new AMI_STREAM*[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. + + T dummykey; // This is for the last arg to + + // AMI_single_merge() + // which necessitated due to type unificatuon problems + + // The number of substreams to be processed at any merge level. + arity_t substream_count; + + for (substream_count = original_substreams; + substream_count > 1; + substream_count = (substream_count + merge_arity - 1) + / merge_arity) { + + // Set up to process a given level. +//RAKESH + tp_assert (len == check_size, + "Current level stream not same length as input." << + "\n\tlen = " << len << + "\n\tcurrent_input->stream_len() = " << + check_size << ".\n"); + + check_size = 0; + + // 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. + + if (substream_count <= merge_arity) { + +//RAKESH Open up the substream_count streams in which the +// the runs input to the current merge level are packed +// The names of these streams (storing the input runs) +// can be constructed from prefix_name[k % 2] + + for (ii = merge_arity - substream_count; ii < merge_arity; + ii++) { + +#ifndef BTE_IMP_USER_DEFINED + + stream_name_generator (working_disk, + prefix_name[k % 2], + (int) ii, new_stream_name); + +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[k % 2], + (int) ii, new_stream_name); +#endif + + current_input[ii] = new AMI_STREAM < T > (new_stream_name); + current_input[ii]->persist (PERSIST_DELETE); + + } + + // Merge them into the output stream. + + ae = AMI_single_merge ( + (current_input + merge_arity - + substream_count), substream_count, + outstream); + + if (ae != AMI_ERROR_NO_ERROR) { + TP_LOG_FATAL_ID ("AMI_single_merge error " << + ae << " returned by AMI_single_merge()"); + return ae; + } + // Delete the streams input to the above merge. + + for (ii = merge_arity - substream_count; + ii < merge_arity; ii++) { + + if (current_input[ii]) { + delete current_input[ii]; + + current_input[ii] = NULL; + } + + } + + if (current_input) { + delete[]current_input; + current_input = NULL; + } + if (the_substreams) { + delete[]the_substreams; + the_substreams = NULL; + } + + } else { + + TP_LOG_DEBUG_ID ("Merging substreams to intermediate streams."); + + // Create the array of merge_arity stream pointers that + // will each point to a stream containing runs output + // at the current level k. + + intermediate_tmp_stream = new AMI_STREAM*[merge_arity]; + +//RAKESH Open up the merge_arity streams in which the +// the runs input to the current merge level are packed +// The names of these streams (storing the input runs) +// can be constructed from prefix_name[k % 2] + + for (ii = 0; ii < merge_arity; ii++) { + +// new_name_from_prefix(prefix_name[k % 2],(int) ii, +// new_stream_name); + +#ifndef BTE_IMP_USER_DEFINED + stream_name_generator (working_disk, + prefix_name[k % 2], + (int) ii, new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[k % 2], + (int) ii, new_stream_name); +#endif + + current_input[ii] = new AMI_STREAM < T > (new_stream_name); + + current_input[ii]->persist (PERSIST_DELETE); + + } + + // 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. +//RAKESH +// current_input->seek(0); + + current_stream = merge_arity - 1; + + //For the first stream that we use to pack some + //of the output runs of the current merge level k. + +// new_name_from_prefix(prefix_name[(k+1) % 2],0, +// new_stream_name); + +#ifndef BTE_IMP_USER_DEFINED + + stream_name_generator (working_disk, + prefix_name[(k + 1) % 2], + current_stream, new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[(k + 1) % 2], + current_stream, new_stream_name); +#endif + + intermediate_tmp_stream[current_stream] = new + AMI_STREAM < T > (new_stream_name); + + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + + int remaining_number_of_output_runs = + (substream_count + merge_arity - 1) / merge_arity; + + for (ii_streams = 0; ii_streams < merge_arity; ii_streams++) { + // If there are 12 runs to be distributed among 5 streams, + // the first three get 2 and the last two get 3 runs + + if (ii_streams < + (merge_arity - + (remaining_number_of_output_runs % merge_arity))) + + desired_runs_in_stream[ii_streams] = + remaining_number_of_output_runs / merge_arity; + + else + desired_runs_in_stream[ii_streams] = + (remaining_number_of_output_runs + + merge_arity - 1) / merge_arity; + + Sub_Start(ii_streams) = 0; + + } + + runs_in_current_stream = 0; + unsigned int merge_number = 0; + + // 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; ii++) { + + if (run_lengths(k % 2, merge_arity - 1 - jj, merge_number) + != 0) { + + sub_start = Sub_Start(merge_arity - 1 - jj); + + sub_end = sub_start + + run_lengths(k % 2, merge_arity - 1 - + jj, merge_number) - 1; + + Sub_Start(merge_arity - 1 - jj) += + run_lengths(k % 2, merge_arity - 1 - + jj, merge_number); + + run_lengths(k % 2, merge_arity - 1 - jj, merge_number) + = 0; + } else { + //This weirdness is caused by the way bte substream + //constructor was designed. + + sub_end = Sub_Start(merge_arity - 1 - jj) - 1; + sub_start = sub_end + 1; + + ii--; + + } + + //Open the new substream + current_input[merge_arity - 1 - + jj]->new_substream (AMI_READ_STREAM, + sub_start, sub_end, + (AMI_stream_base < T > **) + (the_substreams + jj)); + + // The substreams are read-once. + // If we've got all we can handle or we've seen + // them all, then merge them. + + if ((jj >= merge_arity - 1) || (ii == substream_count - 1)) { + + tp_assert (jj <= merge_arity - 1, + "Index got too large."); + + //Check if the stream into which runs are cuurently + //being packed has got its share of runs. If yes, + //delete that stream and construct a new stream + //appropriately. + + if (desired_runs_in_stream[current_stream] == + runs_in_current_stream) { + + //Make sure that the deleted stream persists on disk. + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + + delete intermediate_tmp_stream[current_stream]; + + current_stream = (current_stream + merge_arity - 1) + % merge_arity; + + // Unless the current level is over, we've to generate + //a new stream for the next set of runs. + + if (remaining_number_of_output_runs > 0) { + +// new_name_from_prefix(prefix_name[(k+1) % 2], +// current_stream, new_stream_name); + +#ifndef BTE_IMP_USER_DEFINED + stream_name_generator (working_disk, + prefix_name[(k + 1) % 2], + (int) current_stream, + new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[(k + 1) % 2], + (int) current_stream, + new_stream_name); +#endif + + intermediate_tmp_stream[current_stream] = new + AMI_STREAM < T > (new_stream_name); + + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + runs_in_current_stream = 0; + } + } + + ae = AMI_single_merge (the_substreams, + jj + 1, + intermediate_tmp_stream + [current_stream]); + + if (ae != AMI_ERROR_NO_ERROR) { + TP_LOG_DEBUG_ID ("AMI_single_merge error"); + return ae; + } + + for (ii_streams = 0; ii_streams < jj + 1; ii_streams++) + run_lengths((k + 1) % 2, current_stream, + runs_in_current_stream) += + the_substreams[ii_streams]->stream_len (); + + merge_number++; + + //Decrement the counter corresp to number of runs + // still to be formed at current level + + remaining_number_of_output_runs--; + + // Delete input substreams. jj is currently the index + // of the largest. + + for (ii_streams = 0; ii_streams < jj + 1; ii_streams++) { + if (the_substreams[ii_streams]) { + delete the_substreams[ii_streams]; + + the_substreams[ii_streams] = NULL; + } + } + + jj = 0; + +//RAKESH The number of runs in the current_stream +// goes up by 1. + + runs_in_current_stream++; + + } else { + jj++; + } + + } + + if (intermediate_tmp_stream[current_stream]) { + delete intermediate_tmp_stream[current_stream]; + + intermediate_tmp_stream[current_stream] = NULL; + } + // Get rid of the current input streams and use the ones + //output at the current level. +//RAKESH + + for (ii = 0; ii < merge_arity; ii++) + if (current_input[ii]) { + delete current_input[ii]; + } + if (current_input) { + delete[]current_input; + current_input = NULL; + } + + current_input = (AMI_STREAM < T > **)intermediate_tmp_stream; + + } + + k++; + + } + + //Monitoring prints. + TP_LOG_DEBUG_ID ("Number of passes incl run formation is " << k + + 1 ); + + return AMI_ERROR_NO_ERROR; + + } + TP_LOG_DEBUG_ID ("AMI_partition_and_merge_stream END"); +} + +//------------------------------------------------------------ +//This is a polymorph of AMI_partition_and_merge in ami_merge.h;divide +//the input stream in substreams, merge each substream recursively, +//and merge them together using AMI_single_merge(AMI_STREAM **, +//arity_t , AMI_STREAM *, int , KEY) +//------------------------------------------------------------ +template < class T, class KEY > +AMI_err +AMI_partition_and_merge (AMI_STREAM < T > *instream, + AMI_STREAM < T > *outstream, + int keyoffset, KEY dummykey) +{ + AMI_err ae; + TPIE_OS_OFFSET len; + size_t sz_avail, sz_stream; + size_t sz_substream; + + unsigned int ii, jj; + int ii_streams; + + char *working_disk; + + TP_LOG_DEBUG_ID ("AMI_partition_and_merge_Key: start"); + + // Figure out how much memory we've got to work with. + + sz_avail = MM_manager.memory_available (); + + //Conservatively assume that the memory for buffers for + //the two streams is unallocated; so we need to subtract. + + if ((ae = instream->main_memory_usage (&sz_stream, + MM_STREAM_USAGE_MAXIMUM)) != + AMI_ERROR_NO_ERROR) { + return ae; + } + + if ((ae = instream->main_memory_usage (&sz_substream, + MM_STREAM_USAGE_OVERHEAD)) != + AMI_ERROR_NO_ERROR) { + + return ae; + } + + sz_avail -= 2 * sz_stream; + + working_disk = tpie_tempnam ("AMI"); + //TP_LOG_DEBUG_ID(working_disk); + + // 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 (); + instream->seek (0); + + if ((len * sizeof (T)) <= sz_avail) { + + if (len * (sizeof (T) * sizeof (qsort_item < KEY >)) > sz_avail) + // ie if you have dont have space for separate + // keysorting (good cache performance) followed by permuting + { + + T *next_item; + + TP_LOG_DEBUG_ID ("pre new"); + T *mm_stream = new T[(TPIE_OS_SIZE_T)len]; + + TP_LOG_DEBUG_ID ("post new"); + + for (int i = 0; i < len; i++) { + if ((ae = instream->read_item (&next_item)) != + AMI_ERROR_NO_ERROR) return ae; + mm_stream[i] = *next_item; + } + + quick_sort_op ((T *) mm_stream, (TPIE_OS_SIZE_T)len); + + for (int i = 0; i < len; i++) { + if ((ae = outstream->write_item (mm_stream[i])) + != AMI_ERROR_NO_ERROR) + return ae; + } + TP_LOG_DEBUG_ID ("pre delete"); + if (mm_stream) { + delete[]mm_stream; + mm_stream = NULL; + } + TP_LOG_DEBUG_ID ("post delete"); + } else { + //Use qsort on keys followed by permuting + TP_LOG_DEBUG_ID ("pre new"); + T *mm_stream = new T[(TPIE_OS_SIZE_T)len]; + + qsort_item < KEY > *qs_array = new qsort_item [(TPIE_OS_SIZE_T)len]; + TP_LOG_DEBUG_ID ("post new"); + T *next_item; + + for (int i = 0; i < len; i++) { + if ((ae = instream->read_item (&next_item)) != + AMI_ERROR_NO_ERROR) return ae; + mm_stream[i] = *next_item; + qs_array[i].keyval = *(KEY *) ((char *) next_item + keyoffset); + qs_array[i].source = i; + } + + quick_sort_op ((qsort_item < KEY > *)qs_array, (TPIE_OS_SIZE_T)len); + + for (int i = 0; i < len; i++) { + if ( + (ae = + outstream->write_item (mm_stream[qs_array[i].source])) != + AMI_ERROR_NO_ERROR) return ae; + } + TP_LOG_DEBUG_ID ("pre delete"); + if (mm_stream) { + delete[]mm_stream; + mm_stream = NULL; + } + if (qs_array) { + delete[]qs_array; + qs_array = NULL; + } + TP_LOG_DEBUG_ID ("post delete"); + } + + TP_LOG_DEBUG_ID ("AMI_partition_and_merge_Key: done"); + return AMI_ERROR_NO_ERROR; + + } else { + + // The number of substreams that the original input stream + // will be split into. + + arity_t original_substreams; + + // The length, in terms of stream objects of type T, of the + // original substreams of the input stream. The last one may + // be shorter than this. + + TPIE_OS_OFFSET sz_original_substream; + + // The initial temporary stream, to which substreams of the + // original input stream are written. + + //RAKESH + AMI_STREAM < T > **initial_tmp_stream; + + // The number of substreams that can be merged together at once. + + arity_t merge_arity; + + // 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. + + //RAKESH + AMI_STREAM < T > **current_input; + + // The output stream for the current level if it is not outstream. + + //RAKESH + AMI_STREAM < T > **intermediate_tmp_stream; + + //RAKESH FIX THIS: Need to generate random strings using + //tmpname() or something like that. + char *prefix_name[] = { "_0_", "_1_" }; + + // The size of substreams of *current_input that are being + // merged. The last one may be smaller. This value should be + // sz_original_substream * (merge_arity ** k) where k is the + // number of iterations the loop has gone through. + + //Merge Level + 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. + +//RAKESH +// In our case merge_arity is determined differently than in the original +// implementation of AMI_partition_and_merge since we use several streams +// in each level. +// In our case net main memory required to carry out an R-way merge is +// (R+1)*MM_STREAM_USAGE_MAXIMUM {R substreams for input runs, 1 stream for output} +// + R*MM_STREAM_USAGE_OVERHEAD {One stream for each active input run: but while +// the substreams use buffers, streams don't} +// + (R+1)*m_obj->space_usage_per_stream(); +// +// The net memory usage for an R-way merge is thus +// R*(sz_stream + sz_substeam + m_obj->space_usage_per_stream()) + sz_stream + +// m_obj->space_usage_per_stream(); +// + + //To support a binary merge, need space for max_stream_usage + //for at least three stream objects. + + if (sz_avail <= 3 * (sz_stream + sz_substream + + sizeof (merge_heap_element < KEY >)) + //+ sz_stream + sizeof(merge_heap_element) + ) { + + TP_LOG_FATAL_ID + ("Insufficient memory in AMI_partition_and_merge_Key()"); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + + sz_original_substream = + (sz_avail) / (sizeof (T) + sizeof (qsort_item < KEY >)); + + // 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_original_substream = sz_chunk_size * + ((sz_original_substream + sz_chunk_size - 1) / sz_chunk_size); + } + + original_substreams = static_cast((len + sz_original_substream - 1) / + sz_original_substream); + + // Account for the space that a merge object will use. + + { + //Availabe memory for input stream objects is given by + //sz_avail minus the space occupied by output stream objects. + TPIE_OS_SIZE_T sz_avail_during_merge = sz_avail - + + sz_stream - sz_substream; + + //This conts the per-input stream memory cost. + TPIE_OS_SIZE_T sz_stream_during_merge = sz_stream + sz_substream + + sizeof (merge_heap_element < KEY >); + + //Compute merge arity + merge_arity = static_cast(sz_avail_during_merge / 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 <= 5) { + 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_partition_and_merge_Key(): merge arity = " << + merge_arity ); + + if (merge_arity < 2) { + + TP_LOG_FATAL_ID + ("Insufficient memory for AMI_partition_and_merge_Key()"); + + 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) original_substreams) / + 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 >= original_substreams, + "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_original_substream, + "Size of original streams increased."); + + sz_original_substream = (size_t) new_sz_original_substream; + + TP_LOG_DEBUG_ID ("Memory constraints set original substreams = " << + original_substreams << '\n'); + + original_substreams = (len + sz_original_substream - 1) / + sz_original_substream; + + TP_LOG_DEBUG_ID ("Tree height constraints set original substreams = " + << original_substreams << '\n'); + } + +#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. + + // Comment: (jan) Use VarArray for ANSI-compliance. + + // unsigned int run_lengths[2][merge_arity] + // [ (original_substreams + merge_arity - 1) / merge_arity]; + // int Sub_Start[merge_arity]; + + // End Comment. + + VarArray3D + run_lengths(2, merge_arity, + (original_substreams + merge_arity - 1) / merge_arity); + + VarArray1D Sub_Start(merge_arity); + + // Comment: (jan) initialization is done by the VarArray constructor. + + // memset ((void *) run_lengths, 0, + // 2 * merge_arity * ((original_substreams + merge_arity - 1) / + // merge_arity) * sizeof (unsigned int)); + + // End Comment. + + initial_tmp_stream = new AMI_STREAM*[merge_arity]; + TP_LOG_DEBUG_ID ("pre new"); + mm_stream = new T[(TPIE_OS_SIZE_T)sz_original_substream]; + + qsort_item < KEY > *qs_array = + new qsort_item[(TPIE_OS_SIZE_T)sz_original_substream]; + TP_LOG_DEBUG_ID ("post new"); + + tp_assert (mm_stream != NULL, "Misjudged available main memory."); + + if (mm_stream == NULL) { + + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + + instream->seek (0); + + tp_assert (original_substreams * sz_original_substream - len < + sz_original_substream, + "Total substream length too long or too many."); + + tp_assert (len - (original_substreams - 1) * sz_original_substream <= + sz_original_substream, + "Total substream length too short or too few."); + +//RAKESH + TPIE_OS_OFFSET check_size = 0; + int current_stream = merge_arity - 1; + + int runs_in_current_stream = 0; + int *desired_runs_in_stream = new int[merge_arity]; + char new_stream_name[BTE_STREAM_PATH_NAME_LEN]; + + //For the first stream: + + for (ii_streams = 0; ii_streams < (int) merge_arity; ii_streams++) { + + //Figure out how many runs go in each one of merge_arity streams? + // If there are 12 runs to be distributed among 5 streams, the first + //three get 2 and the last two get 3 runs + + if (ii_streams < + (int) (merge_arity - + (original_substreams % + merge_arity))) desired_runs_in_stream[ii_streams] = + original_substreams / merge_arity; + + else + desired_runs_in_stream[ii_streams] = + (original_substreams + merge_arity - 1) / merge_arity; + } + +#ifndef BTE_IMP_USER_DEFINED + +// new_name_from_prefix(prefix_name[0],current_stream, new_stream_name); + + //The assumption here is that working_disk is the name of the specific + //directory in which the temporary/intermediate streams will be made. + //By default, I think we shd + + stream_name_generator (working_disk, + prefix_name[0], + current_stream, new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[0], + current_stream, new_stream_name); + +#endif + + initial_tmp_stream[current_stream] = + new AMI_STREAM < T > (new_stream_name); + + initial_tmp_stream[current_stream]->persist (PERSIST_PERSISTENT); + + ii = 0; + while (ii < original_substreams) { + TPIE_OS_SIZE_T mm_len; + + // Make sure that the current_stream is supposed to get a run + + if (desired_runs_in_stream[current_stream] > + runs_in_current_stream) { + if (ii == original_substreams - 1) { + mm_len = static_cast(len % sz_original_substream); + + // If it is an exact multiple, then the mod will come + // out 0, which is wrong. + + if (!mm_len) { + mm_len = static_cast(sz_original_substream); + } + } else { + mm_len = static_cast(sz_original_substream); + } + +#if DEBUG_ASSERTIONS + TPIE_OS_OFFSET mm_len_bak = mm_len; +#endif + + // Read a memory load out of the input stream one item at a time, + // fill up the key array at the same time. + + { + T *next_item; + + for (int i = 0; i < mm_len; i++) { + if ((ae = instream->read_item (&next_item)) != + AMI_ERROR_NO_ERROR) return ae; + mm_stream[i] = *next_item; + qs_array[i].keyval = + *(KEY *) ((char *) next_item + keyoffset); + qs_array[i].source = i; + } + + //Sort the key array. + + quick_sort_op ((qsort_item < KEY > *)qs_array, mm_len); + + //Now permute the memoryload as per the sorted key array. + + for (int i = 0; i < mm_len; i++) { + if ( + (ae = + initial_tmp_stream[current_stream]->write_item + (mm_stream[qs_array[i].source])) + != AMI_ERROR_NO_ERROR) + return ae; + + } + + run_lengths(0, current_stream, runs_in_current_stream) = + mm_len; + + } + + runs_in_current_stream++; + ii++; + + } +//RAKESH + if (runs_in_current_stream == + desired_runs_in_stream[current_stream]) { + + check_size += + initial_tmp_stream[current_stream]->stream_len (); + + // We do not want old streams hanging around + // occuping memory. We know how to get the streams + // since we can generate their names + + if (initial_tmp_stream[current_stream]) { + delete initial_tmp_stream[current_stream]; + + initial_tmp_stream[current_stream] = NULL; + } + + if ((int) check_size < instream->stream_len ()) { + + current_stream = (current_stream + merge_arity - 1) + % merge_arity; + +#ifndef BTE_IMP_USER_DEFINED + // new_name_from_prefix(prefix_name[0],current_stream, new_stream_name); + + stream_name_generator (working_disk, + prefix_name[0], + current_stream, new_stream_name); + +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[0], + current_stream, new_stream_name); +#endif + + initial_tmp_stream[current_stream] = + new AMI_STREAM < T > (new_stream_name); + + initial_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + + // Number of runs packed into + // the stream just constructed now + + runs_in_current_stream = 0; + } + } + + } + + if (initial_tmp_stream[current_stream]) { + delete initial_tmp_stream[current_stream]; + + initial_tmp_stream[current_stream] = NULL; + } + TP_LOG_DEBUG_ID ("pre delete"); + if (mm_stream) { + delete[]mm_stream; + mm_stream = NULL; + } + if (qs_array) { + delete[]qs_array; + qs_array = NULL; + } + TP_LOG_DEBUG_ID ("post delete"); + + // 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 () == check_size, + "Stream lengths do not match:" << + "\n\tinstream->stream_len() = " << instream->stream_len () + << "\n\tinitial_tmp_stream->stream_len() = " << check_size + << ".\n"); + + //We now delete the instream; note that it will be wiped off + //disk if instream->persistence is set to PERSIST_DELETE + //delete instream; + + // Set up the loop invariants for the first iteration of hte + // main loop. + + current_input = initial_tmp_stream; + + // Pointers to the substreams that will be merged. +//RAKESH + AMI_STREAM < T > **the_substreams = + new AMI_STREAM*[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. + + KEY dummykey; // This is for the last arg to + + // AMI_partition_and_merge_Key() + // which necessitated due to type unificatuon problems + + // The number of substreams to be processed at any merge level. + arity_t substream_count; + + //Monitoring prints. + + TP_LOG_DEBUG_ID ("Number of runs from run formation is " << + original_substreams ); + TP_LOG_DEBUG_ID ("Merge arity is " << merge_arity ); + + for (substream_count = original_substreams; + substream_count > 1; + substream_count = (substream_count + merge_arity - 1) + / merge_arity) { + + // Set up to process a given level. +//RAKESH + tp_assert (len == check_size, + "Current level stream not same length as input." << + "\n\tlen = " << len << + "\n\tcurrent_input->stream_len() = " << + check_size << ".\n"); + + check_size = 0; + + // 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. + + if (substream_count <= merge_arity) { + +//RAKESH Open up the substream_count streams in which the +// the runs input to the current merge level are packed +// The names of these streams (storing the input runs) +// can be constructed from prefix_name[k % 2] + + for (ii = merge_arity - substream_count; ii < merge_arity; + ii++) { + +#ifndef BTE_IMP_USER_DEFINED + + stream_name_generator (working_disk, + prefix_name[k % 2], + (int) ii, new_stream_name); + +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[k % 2], + (int) ii, new_stream_name); +#endif + + current_input[ii] = new AMI_STREAM < T > (new_stream_name); + current_input[ii]->persist (PERSIST_DELETE); + + } + + // Merge them into the output stream. + + ae = AMI_single_merge ( + (current_input + merge_arity - + substream_count), substream_count, + outstream, keyoffset, dummykey); + + if (ae != AMI_ERROR_NO_ERROR) { + + TP_LOG_FATAL_ID ("AMI_ERROR " << + ae << " returned by AMI_single_merge()"); + return ae; + } + // Delete the streams input to the above merge. + + for (ii = merge_arity - substream_count; + ii < merge_arity; ii++) { + if (current_input[ii]) { + delete current_input[ii]; + + current_input[ii] = NULL; + } + + } + + if (current_input) { + delete[]current_input; + current_input = NULL; + } + + if (the_substreams) { + delete[]the_substreams; + the_substreams = NULL; + } + + } else { + + TP_LOG_DEBUG_ID ("Merging substreams to intermediate streams."); + + // Create the array of merge_arity stream pointers that + // will each point to a stream containing runs output + // at the current level k. + + intermediate_tmp_stream = new AMI_STREAM*[merge_arity]; + +//RAKESH Open up the merge_arity streams in which the +// the runs input to the current merge level are packed +// The names of these streams (storing the input runs) +// can be constructed from prefix_name[k % 2] + + for (ii = 0; ii < merge_arity; ii++) { + +// new_name_from_prefix(prefix_name[k % 2],(int) ii, +// new_stream_name); + +#ifndef BTE_IMP_USER_DEFINED + stream_name_generator (working_disk, + prefix_name[k % 2], + (int) ii, new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[k % 2], + (int) ii, new_stream_name); +#endif + + current_input[ii] = new AMI_STREAM < T > (new_stream_name); + + current_input[ii]->persist (PERSIST_DELETE); + + } + + // 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. +//RAKESH +// current_input->seek(0); + + current_stream = merge_arity - 1; + + //For the first stream that we use to pack some + //of the output runs of the current merge level k. + +// new_name_from_prefix(prefix_name[(k+1) % 2],0, +// new_stream_name); + +#ifndef BTE_IMP_USER_DEFINED + + stream_name_generator (working_disk, + prefix_name[(k + 1) % 2], + current_stream, new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[(k + 1) % 2], + current_stream, new_stream_name); +#endif + + intermediate_tmp_stream[current_stream] = new + AMI_STREAM < T > (new_stream_name); + + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + + int remaining_number_of_output_runs = + (substream_count + merge_arity - 1) / merge_arity; + + for (ii_streams = 0; ii_streams < (int) merge_arity; + ii_streams++) { + // If there are 12 runs to be distributed among 5 streams, + // the first three get 2 and the last two get 3 runs + + if (ii_streams < + (int) (merge_arity - + (remaining_number_of_output_runs % merge_arity))) + + desired_runs_in_stream[ii_streams] = + remaining_number_of_output_runs / merge_arity; + + else + desired_runs_in_stream[ii_streams] = + (remaining_number_of_output_runs + + merge_arity - 1) / merge_arity; + + Sub_Start(ii_streams) = 0; + + } + + runs_in_current_stream = 0; + unsigned int merge_number = 0; + + // 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; ii++) { + + if (run_lengths(k % 2, merge_arity - 1 - jj, merge_number) + != 0) { + + sub_start = Sub_Start(merge_arity - 1 - jj); + + sub_end = sub_start + + run_lengths(k % 2, merge_arity - 1 - + jj, merge_number) - 1; + + Sub_Start(merge_arity - 1 - jj) += + run_lengths(k % 2, merge_arity - 1 - + jj, merge_number); + + run_lengths(k % 2, merge_arity - 1 - jj, merge_number) + = 0; + } else { + //This weirdness is caused by the way bte substream + //constructor was designed. + + sub_end = Sub_Start(merge_arity - 1 - jj) - 1; + sub_start = sub_end + 1; + + ii--; + + } + + //Open the new substream + current_input[merge_arity - 1 - + jj]->new_substream (AMI_READ_STREAM, + sub_start, sub_end, + (AMI_stream_base < T > **) + (the_substreams + jj)); + + // The substreams are read-once. + // If we've got all we can handle or we've seen + // them all, then merge them. + + if ((jj >= merge_arity - 1) || (ii == substream_count - 1)) { + + tp_assert (jj <= merge_arity - 1, + "Index got too large."); + + //Check if the stream into which runs are cuurently + //being packed has got its share of runs. If yes, + //delete that stream and construct a new stream + //appropriately. + + if (desired_runs_in_stream[current_stream] == + runs_in_current_stream) { + + //Make sure that the deleted stream persists on disk. + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + + delete intermediate_tmp_stream[current_stream]; + + current_stream = (current_stream + merge_arity - 1) + % merge_arity; + + // Unless the current level is over, we've to generate + //a new stream for the next set of runs. + + if (remaining_number_of_output_runs > 0) { + +// new_name_from_prefix(prefix_name[(k+1) % 2], +// current_stream, new_stream_name); + +#ifndef BTE_IMP_USER_DEFINED + stream_name_generator (working_disk, + prefix_name[(k + 1) % 2], + (int) current_stream, + new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[(k + 1) % 2], + (int) current_stream, + new_stream_name); +#endif + + intermediate_tmp_stream[current_stream] = new + AMI_STREAM < T > (new_stream_name); + + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + runs_in_current_stream = 0; + } + } + + ae = AMI_single_merge (the_substreams, + jj + 1, + intermediate_tmp_stream + [current_stream], keyoffset, + dummykey); + + if (ae != AMI_ERROR_NO_ERROR) { + return ae; + } + + for (ii_streams = 0; ii_streams < (int) jj + 1; + ii_streams++) + run_lengths((k + 1) % 2, current_stream, + runs_in_current_stream) += + the_substreams[ii_streams]->stream_len (); + + merge_number++; + + //Decrement the counter corresp to number of runs + // still to be formed at current level + + remaining_number_of_output_runs--; + + // Delete input substreams. jj is currently the index + // of the largest. + + for (ii_streams = 0; ii_streams < (int) jj + 1; + ii_streams++) { + if (the_substreams[ii_streams]) { + delete the_substreams[ii_streams]; + + the_substreams[ii_streams] = NULL; + } + } + + jj = 0; + +//RAKESH The number of runs in the current_stream +// goes up by 1. + + runs_in_current_stream++; + + } else { + jj++; + } + + } + + if (intermediate_tmp_stream[current_stream]) { + delete intermediate_tmp_stream[current_stream]; + + intermediate_tmp_stream[current_stream] = NULL; + } + // Get rid of the current input streams and use the ones + //output at the current level. +//RAKESH + + for (ii = 0; ii < merge_arity; ii++) + if (current_input[ii]) + delete current_input[ii]; + + if (current_input) { + delete[]current_input; + current_input = NULL; + } + + current_input = (AMI_STREAM < T > **)intermediate_tmp_stream; + + } + + k++; + + } + + //Monitoring prints. + + TP_LOG_DEBUG_ID ("Number of passes incl run formation is " << k + + 1 ); + + TP_LOG_DEBUG_ID ("AMI_partition_and_merge_Key: done"); + return AMI_ERROR_NO_ERROR; + + } + + assert (0); // no return value - die - R.. +} + +//------------------------------------------------------------ +template < class T, class KEY > +AMI_err AMI_replacement_selection_and_merge_Key (AMI_STREAM < T > + *instream, + AMI_STREAM < T > + *outstream, + int keyoffset, + KEY dummykey) +{ + AMI_err ae; + TPIE_OS_OFFSET len; + size_t sz_avail, sz_stream; + size_t sz_substream; + + unsigned int ii, jj, kk; + int ii_streams; + +#ifndef BTE_IMP_USER_DEFINED + char *working_disk; +#endif + + // Figure out how much memory we've got to work with. + + sz_avail = MM_manager.memory_available (); + +#ifndef BTE_IMP_USER_DEFINED + working_disk = tpie_tempnam ("AMI"); + //TP_LOG_DEBUG_ID(working_disk); +#endif + + // 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 (); + instream->seek (0); + + if ((len * sizeof (T)) <= sz_avail) { + + if (len * (sizeof (T) * sizeof (qsort_item < KEY >)) > sz_avail) + // ie if you have dont have space for separate + // keysorting (good cache performance) followed by permuting + { + + T *next_item; + + TP_LOG_DEBUG_ID ("pre new"); + T *mm_stream = new T[len]; + + TP_LOG_DEBUG_ID ("post new"); + + for (int i = 0; i < len; i++) { + if ((ae = instream->read_item (&next_item)) != + AMI_ERROR_NO_ERROR) return ae; + mm_stream[i] = *next_item; + } + + quick_sort_op ((T *) mm_stream, len); + + for (int i = 0; i < len; i++) { + if ((ae = outstream->write_item (mm_stream[i])) + != AMI_ERROR_NO_ERROR) + return ae; + } + TP_LOG_DEBUG_ID ("pre delete"); + if (mm_stream) { + delete[]mm_stream; + mm_stream = NULL; + } + TP_LOG_DEBUG_ID ("post delete"); + } else { + //Use qsort on keys followed by permuting + + TP_LOG_DEBUG_ID ("pre new"); + T *mm_stream = new T[len]; + + TP_LOG_DEBUG_ID ("post new"); + qsort_item < KEY > *qs_array = new qsort_item[len]; + TP_LOG_DEBUG_ID ("post new"); + T *next_item; + + for (int i = 0; i < len; i++) { + if ((ae = instream->read_item (&next_item)) != + AMI_ERROR_NO_ERROR) return ae; + mm_stream[i] = *next_item; + qs_array[i].keyval = *(KEY *) ((char *) next_item + keyoffset); + qs_array[i].source = i; + } + + quick_sort_op ((qsort_item < KEY > *)qs_array, len); + + for (int i = 0; i < len; i++) { + if ( + (ae = + outstream->write_item (mm_stream[qs_array[i].source])) != + AMI_ERROR_NO_ERROR) return ae; + } + TP_LOG_DEBUG_ID ("pre delete"); + if (mm_stream) { + delete[]mm_stream; + mm_stream = NULL; + } + TP_LOG_DEBUG_ID ("post delete"); + if (qs_array) { + delete[]qs_array; + qs_array = NULL; + } + TP_LOG_DEBUG_ID ("post delete"); + } + + return AMI_ERROR_NO_ERROR; + + } else { + + // The number of substreams that the original input stream + // will be split into + + arity_t original_substreams; + + // The length, in terms of stream objects of type T, of the + // original substreams of the input stream. The last one may + // be shorter than this. + + size_t sz_original_substream; + + // The initial temporary stream, to which substreams of the + // original input stream are written. + + AMI_STREAM < T > **initial_tmp_stream; + + // The number of substreams that can be merged together at once. + + arity_t merge_arity; + + // 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. + + int runs_in_current_stream; + +//RAKESH + AMI_STREAM < T > **current_input; + + // The output stream for the current level if it is not outstream. + +//RAKESH + AMI_STREAM < T > **intermediate_tmp_stream; + + //TO DO +//RAKESH (Hard coded prefixes) Ideally you be asking TPIE to give new names + char *prefix_name[] = { "_0_", "_1_" }; + char itoa_str[5]; + + // The size of substreams of *current_input that are being + // merged. The last one may be smaller. This value should be + // sz_original_substream * (merge_arity ** k) where k is the + // number of iterations the loop has gone through. + + size_t 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. + + size_t mergeoutput_v; + + if ((ae = instream->main_memory_usage (&sz_stream, + MM_STREAM_USAGE_MAXIMUM)) != + AMI_ERROR_NO_ERROR) { + return ae; + } + + if ((ae = instream->main_memory_usage (&sz_substream, + MM_STREAM_USAGE_OVERHEAD)) != + AMI_ERROR_NO_ERROR) { + + return ae; + } + //Conservatively assume that the input and output streams + //have not been accounted for in the bte_stream. + + sz_avail -= 2 * sz_stream; + + sz_original_substream = sz_avail - 2 * sz_stream; + +// Here the above var is in bytes: in AMI_partition_and_merge, +// its in number of items of type T. + +//RAKESH +// In our case merge_arity is determined differently than in the original +// implementation of AMI_partition_and_merge since we use several streams +// in each level. +// In our case net main memory required to carry out an R-way merge is +// (R+1)*MM_STREAM_USAGE_MAXIMUM {R substreams for input runs, 1 stream for output} +// + R*MM_STREAM_USAGE_OVERHEAD {One stream for each active input run: but while +// the substreams use buffers, streams don't} +// + (R+1)*m_obj->space_usage_per_stream(); +// +// The net memory usage for an R-way merge is thus +// R*(sz_stream + sz_substeam + m_obj->space_usage_per_stream()) + sz_stream + +// m_obj->space_usage_per_stream(); +// + + //We can probably make do with a little less memory + //if there is only a single binary merge pass required + //but its too specialized a case to optimize for. + + if (sz_avail <= 3 * (sz_stream + sz_substream + + sizeof (merge_heap_element < KEY >)) + //+ sz_stream + sizeof(merge_heap_element) + ) { + + TP_LOG_FATAL_ID + ("Insufficient Memory for AMI_replacement_selection_and_merge_Key()"); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + // 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. + + { + size_t sz_chunk_size = instream->chunk_size (); + + //RAKESH: Why is this the ceiling instead of being the floor? + + sz_original_substream = sz_chunk_size * + ((sz_original_substream + sz_chunk_size - 1) / sz_chunk_size); + } + +//The foll qty is a "to be determined" qty since the run lengths +// resulting from replacement selection are unknown. + + // Account for the space that a merge object will use. + + { + size_t sz_avail_during_merge = + sz_avail - sz_stream - sz_substream - sz_stream - + sizeof (merge_heap_element < KEY >); + + size_t sz_stream_during_merge = sz_stream + sz_substream + + sizeof (merge_heap_element < KEY >); + + merge_arity = sz_avail_during_merge / 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_replacement_selection_and_merge(): merge arity = " + << merge_arity ); + + if (merge_arity < 2) { + TP_LOG_FATAL_ID + ("Insufficient Memory for AMI_replacement_selection_and_merge_Key()"); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + // Create a temporary stream, then iterate through the + // substreams, processing each one and writing it to the + // corresponding substream of the temporary stream. +//RAKESH + + instream->seek (0); + + size_t check_size = 0; + + char computed_prefix[BTE_STREAM_PATH_NAME_LEN]; + char new_stream_name[BTE_STREAM_PATH_NAME_LEN]; + + //Compute a prefix that will be sent to the run formation function, + //since that is where the initial runs are formed. + +#ifndef BTE_IMP_USER_DEFINED + strcpy (computed_prefix, working_disk); + //strcat(computed_prefix,"/"); + strcat (computed_prefix, prefix_name[0]); +#endif + +#ifdef BTE_IMP_USER_DEFINED + strcpy (computed_prefix, prefix_name[0]); +#endif + + //Conservatie estimate of the max possible number of runs during + //run formation. + int MaxRuns = instream->stream_len () / + (sz_original_substream / + (sizeof (run_formation_item < KEY >) + sizeof (T))); + + //Arrays to store the number of runs in each of the streams formed + //during each pass and the length of each of the runs. + + int RunsInStream[2][merge_arity], + RunLengths[2][merge_arity][(MaxRuns + merge_arity - 1) / + merge_arity]; + + for (int i = 0; i < merge_arity; i++) { + RunsInStream[0][i] = 0; + RunsInStream[1][i] = 0; + } + + KEY dummykey; // This is only for the last argument to + + //Run_Formation() that was added because of type + //unifcation problems. + + //Call the run formation function. + + if ((ae = Run_Formation_Algo_R_Key (instream, + merge_arity, + initial_tmp_stream, + computed_prefix, + sz_original_substream, + RunsInStream[0], + (int **) RunLengths[0], + (MaxRuns + merge_arity - + 1) / merge_arity, keyoffset, + dummykey)) != AMI_ERROR_NO_ERROR) { + TP_LOG_FATAL_ID ("AMI Error " << + ae << " in Run_Formation_Algo_R_Key()"); + return ae; + + } + // Make sure the total length of the temporary stream is the + // same as the total length of the original input stream. + + arity_t run_count = 0; + + for (int i = 0; i < merge_arity; i++) { + for (int j = 0; j < RunsInStream[0][i]; j++) { + + check_size += RunLengths[0][i][j]; + } + + run_count += RunsInStream[0][i]; + } + + if (check_size != instream->stream_len ()) { + TP_LOG_FATAL_ID + ("Run_Formation_Algo_R_Key() output different from input stream in length"); + return AMI_ERROR_IO_ERROR; + } + + tp_assert (instream->stream_len () == check_size, + "Stream lengths do not match:" << + "\n\tinstream->stream_len() = " << instream->stream_len () + << "\n\tinitial_tmp_stream->stream_len() = " << check_size + << ".\n"); + + //We now delete the instream; note that it will be wiped off + //disk if instream->persistence is set to PERSIST_DELETE + //delete instream; + + // Set up the loop invariants for the first iteration of the + // main loop. + + current_input = new AMI_STREAM *[merge_arity]; + arity_t next_level_run_count; + int run_start[merge_arity]; + + // Pointers to the substreams that will be merged. + +//RAKESH + AMI_STREAM < T > **the_substreams = new AMI_STREAM*[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. + + while (run_count > 1) { + + // Set up to process a given level. +//RAKESH + + // 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. + + if (run_count <= merge_arity) { + +//RAKESH Open up the run_count streams in which the +// the runs input to the current merge level are packed +// The names of these streams (storing the input runs) +// can be constructed from prefix_name[k % 2] + + for (ii = merge_arity - run_count; ii < merge_arity; ii++) { + +#ifndef BTE_IMP_USER_DEFINED + + stream_name_generator (working_disk, + prefix_name[k % 2], + (int) ii, new_stream_name); + +#endif + +#ifdef BTE_IMP_USER_DEFINED + + stream_name_generator ("", + prefix_name[k % 2], + (int) ii, new_stream_name); + +#endif + + current_input[ii] = new AMI_STREAM < T > (new_stream_name); + current_input[ii]->persist (PERSIST_DELETE); + + } + + // Merge them into the output stream. + + ae = AMI_single_merge ( + (current_input + merge_arity - + run_count), run_count, outstream, + keyoffset, dummykey); + + if (ae != AMI_ERROR_NO_ERROR) { + TP_LOG_FATAL_ID ("AMI Error "); + TP_LOG_FATAL (ae); + TP_LOG_FATAL ("AMI_single_merge()"); + return ae; + } + // Delete the substreams. +//RAKESH + + for (ii = merge_arity - run_count; ii < merge_arity; ii++) { + + if (current_input[ii]) + delete current_input[ii]; + + } + + // And the current input, which is an intermediate stream + // of some kind. + + if (current_input) { + delete[]current_input; + current_input = NULL; + } + if (the_substreams) { + delete[]the_substreams; + the_substreams = NULL; + } + + run_count = 1; + + } else { + + TP_LOG_DEBUG_ID + ("Merging substreams to an intermediate stream."); + + // Create the array of merge_arity stream pointers that + // will each point to a stream containing runs output + // at the current level k. + + // Note that the array RunLengths[k % 2][ii] contains lengths of + // the RunsInStream[k % 2][ii] runs in current_input stream + // ii. + + //Number of runs in the next level. + next_level_run_count = + (run_count + merge_arity - 1) / merge_arity; + + intermediate_tmp_stream = new AMI_STREAM*[merge_arity]; + +//RAKESH Open up the merge_arity streams in which the +// the runs input to the current merge level are packed +// The names of these streams (storing the input runs) +// can be constructed from prefix_name[k % 2] + + for (ii = 0; ii < merge_arity; ii++) { + +// new_name_from_prefix(prefix_name[k % 2],(int) ii, +// new_stream_name); + +#ifndef BTE_IMP_USER_DEFINED + + stream_name_generator (working_disk, + prefix_name[k % 2], + (int) ii, new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + + stream_name_generator ("", + prefix_name[k % 2], + (int) ii, new_stream_name); + +#endif + + //Construct the stream + current_input[ii] = new AMI_STREAM < T > (new_stream_name); + + current_input[ii]->persist (PERSIST_DELETE); + + } + + //Stream counter + int current_stream = merge_arity - 1; + + //Construct the first stream that we use to pack some + //of the output runs of the current merge level k. + +#ifndef BTE_IMP_USER_DEFINED + + stream_name_generator (working_disk, + prefix_name[(k + 1) % 2], + current_stream, new_stream_name); +#endif + +#ifdef BTE_IMP_USER_DEFINED + stream_name_generator ("", + prefix_name[(k + 1) % 2], + current_stream, new_stream_name); + +#endif + + intermediate_tmp_stream[current_stream] = + new AMI_STREAM < T > (new_stream_name); + + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + + //Number of output runs that remain to be generated at this level. + int remaining_number_of_output_runs = + (run_count + merge_arity - 1) / merge_arity; + + //Determine the number of runs that will go in each of the streams + //that will be output at this level. + + for (ii_streams = 0; ii_streams < merge_arity; ii_streams++) { + + if (ii_streams < + (merge_arity - + (remaining_number_of_output_runs % merge_arity))) + + RunsInStream[(k + 1) % 2][ii_streams] = + remaining_number_of_output_runs / merge_arity; + + else + RunsInStream[(k + 1) % 2][ii_streams] = + (remaining_number_of_output_runs + merge_arity - 1) / + merge_arity; + run_start[ii_streams] = 0; + + } + + runs_in_current_stream = 0; + + // Loop through the substreams of the current stream, + // merging as many as we can at a time until all are + // done with. + + mergeoutput_v = 0; + int merge_number = 0; + + for (ii = 0, jj = 0; ii < run_count; ii++, jj++) { + + //Runs can be of various lengths; so need to have + //appropriate starting and ending points for substreams. + + sub_start = run_start[merge_arity - 1 - jj]; + + sub_end = sub_start + + RunLengths[k % 2][merge_arity - 1 - jj][merge_number] - + 1; + + run_start[merge_arity - 1 - jj] += + RunLengths[k % 2][merge_arity - 1 - jj][merge_number]; + + //The weirdness below is because of the nature of the + // substream arguments. + + if (sub_end >= + current_input[merge_arity - 1 - jj]->stream_len ()) { + + sub_end = + current_input[merge_arity - 1 - jj]->stream_len () - + 1; + + if (sub_start > + current_input[merge_arity - 1 - jj]->stream_len ()) + + sub_start = sub_end + 1; + + } + + mergeoutput_v += sub_end - sub_start + 1; + + if (sub_end - sub_start + 1 == 0) + ii--; + + //NOTE:If the above condition is true it means that + // the run just encountered is a dummy run; + // the last merge of a pass has + // ( merge_arity - (run_count % merge_arity) ) + // dummy runs; no other merge of the pass has any dummy run. + + current_input[merge_arity - 1 - + jj]->new_substream (AMI_READ_STREAM, + sub_start, sub_end, + (AMI_stream_base < T > **) + (the_substreams + jj)); + + // If we've got all we can handle or we've seen + // them all, then merge them. + + if ((jj >= merge_arity - 1) || (ii == run_count - 1)) { + + tp_assert (jj <= merge_arity - 1, + "Index got too large."); + + //Check to see if the current intermediate_tmp_stream + //contains as many runs as it should; if yes, then + //destroy (with PERSISTENCE) that stream and + //construct the next intermediate_tmp_stream. + + if (RunsInStream[(k + 1) % 2][current_stream] + == runs_in_current_stream) { + + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + + delete intermediate_tmp_stream[current_stream]; + + current_stream = + (current_stream + merge_arity - 1) % merge_arity; + + // Unless the current level is over, we've to + //generate a new stream for the next set of runs. + + if (remaining_number_of_output_runs > 0) { + +#ifndef BTE_IMP_USER_DEFINED + + stream_name_generator (working_disk, + prefix_name[(k + 1) % 2], + (int) current_stream, + new_stream_name); + +#endif + +#ifdef BTE_IMP_USER_DEFINED + + stream_name_generator ("", + prefix_name[(k + 1) % 2], + (int) current_stream, + new_stream_name); + +#endif + + intermediate_tmp_stream[current_stream] = + new AMI_STREAM < T > (new_stream_name); + + intermediate_tmp_stream[current_stream]->persist + (PERSIST_PERSISTENT); + runs_in_current_stream = 0; + } + } + // The merge should append to the output stream, since + // AMI_single_merge() does not rewind the + // output before merging. + + ae = AMI_single_merge (the_substreams, + jj + 1, + intermediate_tmp_stream + [current_stream], keyoffset, + dummykey); + + if (ae != AMI_ERROR_NO_ERROR) { + TP_LOG_FATAL_ID ("AMI Error "); + TP_LOG_FATAL (ae); + TP_LOG_FATAL ("AMI_single_merge()"); + return ae; + } + + RunLengths[(k + 1) % 2][current_stream] + [runs_in_current_stream] = mergeoutput_v; + + merge_number++; + +//RAKESH Decrement the counter corresp to number of runs still to be +// formed at current level + + mergeoutput_v = 0; + remaining_number_of_output_runs--; + + // 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--;) { + if (the_substreams[jj]) { + delete the_substreams[jj]; + + the_substreams[jj] = NULL; + } + } + + // 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."); + +//RAKESH Advance the starting position within each of the +// current_input streams by the input run length +// of merge level k. + +//RAKESH The number of runs in the current_stream +// goes up by 1. + + runs_in_current_stream++; + + } + + } + + if (intermediate_tmp_stream[current_stream]) { + delete intermediate_tmp_stream[current_stream]; + + intermediate_tmp_stream[current_stream] = NULL; + } + // Get rid of the current input stream and use the next one. + + for (ii = 0; ii < merge_arity; ii++) { + if (current_input[ii]) + delete current_input[ii]; + } + + if (current_input) { + delete[]current_input; + current_input = NULL; + } + + current_input = (AMI_STREAM < T > **)intermediate_tmp_stream; + + run_count = next_level_run_count; + } + + k++; + + } + + return AMI_ERROR_NO_ERROR; + } +} + +//------------------------------------------------------------ +template < class T, class KEY > +AMI_err +Run_Formation_Algo_R_Key (AMI_STREAM < T > *instream, + arity_t arity, + AMI_STREAM < T > **outstreams, + char *computed_prefix, + size_t available_mem, + int *LRunsInStream, + int **LRunLengths, + int dim2_LRunLengths, int offset_to_key, + KEY dummykey) +{ + + char local_copy[BTE_STREAM_PATH_NAME_LEN]; + + strcpy (local_copy, computed_prefix); + + AMI_err ami_err; + +//For now we are assuming that the key is of type int +//and that the offset of the key within an item of type +//T is offset_to_key=0 + +//Define the proper structure for algorithm R of Vol 3 + +//What is called "P" in algorithm R in Vol 3. (Avg run length is 2P) + unsigned int Number_P = + available_mem / (sizeof (run_formation_item < KEY >) + sizeof (T)); + + run_formation_item < KEY > *Array_X = new run_formation_item[Number_P]; + T *Item_Array = new T[Number_P]; + + T *ptr_to_record; + unsigned int tempint; + unsigned int Var_T; + unsigned int curr_run_length = 0; + +//We will write first run into stream arity-1, then next run into +//stream arity-2, and so on in a round robin manner. + int current_stream = arity - 1; + + char int_to_string[5], new_stream_name[BTE_STREAM_PATH_NAME_LEN]; + + outstreams = new AMI_STREAM*[arity]; + + int *Cast_Var = (int *) LRunLengths; + int MaxRuns = + instream->stream_len () / (available_mem / + sizeof (run_formation_item < KEY >)); + + int RF_Cntr = 0; + + short RMAX = 0; + short RC = 0; + KEY LASTKEY; //Should be guaranteed to be initialized to something greater than + + //the key value of the first item in instream, for correctness. + + int Q = 0; + short RQ = 0; + + for (unsigned int j = 0; j < Number_P; j++) { + Array_X[j].Loser = j; + Array_X[j].RunNumber = 0; + Array_X[j].ParentExt = (Number_P + j) / 2; + Array_X[j].ParentInt = j / 2; + Array_X[j].RecordPtr = j; + } + + Step_R2: + if (RQ != RC) { + if (RC >= 1) { + + Cast_Var[current_stream * dim2_LRunLengths + + LRunsInStream[current_stream]] = curr_run_length; + + LRunsInStream[current_stream]++; + + delete outstreams[current_stream]; + + current_stream = (current_stream + arity - 1) % arity; + RF_Cntr += curr_run_length; + } + + if (RQ > RMAX) + goto Step_End; + else + RC = RQ; + + // Now construct the possibly previously destroyed stream for + // new run and seek to its end. + + //Compute the name for the stream + sprintf (int_to_string, "%d", current_stream); + strcpy (new_stream_name, local_copy); + strcat (new_stream_name, int_to_string); + + // Use the appropriate constructor. + //BEGIN CONSTRUCT STREAM + +#ifdef BTE_IMP_USER_DEFINED + + outstreams[current_stream] = new AMI_STREAM < T > (new_stream_name); + +#else //! BTE_IMP_USER_DEFINED****************************************************** + + outstreams[current_stream] = new AMI_STREAM < T > (new_stream_name); + +#endif //**************************************************************************** + + //END CONSTRUCT STREAM + + outstreams[current_stream]->persist (PERSIST_PERSISTENT); + + outstreams[current_stream]-> + seek (outstreams[current_stream]->stream_len ()); + + // Now set length of currently being formed run to zero. + + curr_run_length = 0; + + } +// End of Step_R2 + + Step_R3: + if (RQ == 0) { + + if ((ami_err = instream->read_item (&ptr_to_record)) + != AMI_ERROR_NO_ERROR) { + if (ami_err == AMI_ERROR_END_OF_STREAM) { + RQ = RMAX + 1; + goto Step_R5; + } + return ami_err; + } + //Copy the most recently read item into item array loc + // Array_X[Q].RecordPtr + + Item_Array[Array_X[Q].RecordPtr] = *ptr_to_record; + Array_X[Q].Key = *(KEY *) ((char *) ptr_to_record + offset_to_key); + + //The above portion is actually carried out in Step R4 in Vol 3's + // description of Algorithm R. But here we carry it out in Step + // R3 itself so that we can efficiently simulate LASTKEY=Infinity + + //We've made sure that we read the first record from instream + // Now we set LASTKEY to be one more than that first record's key + // so that it simulates LASTKEY=Infinity + + LASTKEY = *(KEY *) ((char *) ptr_to_record + offset_to_key); + ++LASTKEY; //LASTKEY = LASTKEY+1; + + } + + else { + + if ( + (ami_err = + outstreams[current_stream]->write_item (Item_Array + [Array_X[Q]. + RecordPtr])) != + AMI_ERROR_NO_ERROR) { + return ami_err; + } + + LASTKEY = Array_X[Q].Key; + + curr_run_length++; + + // The foll portion is actually carried out in Step R4 in Vol 3's + // description of Algorithm R. But here we carry it out in Step + // R3 itself so that we can efficiently simulate LASTKEY=Infinity + + if ((ami_err = instream->read_item (&ptr_to_record)) + != AMI_ERROR_NO_ERROR) { + if (ami_err == AMI_ERROR_END_OF_STREAM) { + RQ = RMAX + 1; + goto Step_R5; + } + return ami_err; + } + + Item_Array[Array_X[Q].RecordPtr] = *ptr_to_record; + Array_X[Q].Key = *(KEY *) ((char *) ptr_to_record + offset_to_key); + + } + + Step_R4: // Array_X[Q] already contains a new item from input stream. + if (Array_X[Q].Key < LASTKEY) { + + // Array_X[Q].Record cannot go into the present run so : + + RQ = RQ + 1; + if (RQ > RMAX) + RMAX = RQ; + } + + Step_R5: + + Var_T = Array_X[Q].ParentExt; + + Step_R6: + if ( + (Array_X[Var_T].RunNumber < RQ) || + ((Array_X[Var_T].RunNumber == RQ) && + // KEY(LOSER(T)) < KEY(Q) + Array_X[Array_X[Var_T].Loser].Key < Array_X[Q].Key) + ) { + // Swap LOSER(T) and Q + tempint = Array_X[Var_T].Loser; + Array_X[Var_T].Loser = Q; + Q = tempint; + + //Swap RN(T) and RQ + tempint = Array_X[Var_T].RunNumber; + Array_X[Var_T].RunNumber = RQ; + RQ = tempint; + } + + Step_R7: + if (Var_T == 1) { + goto Step_R2; + } else { + Var_T = Array_X[Var_T].ParentInt; + goto Step_R6; + } + + Step_End:delete Array_X; + delete Item_Array; + + delete[]outstreams; + + return AMI_ERROR_NO_ERROR; + +} + +#endif // _AMI_OPTIMIZED_MERGE_H diff --git a/fastlib/u/nvasil/tpie/ami_optimized_sort.h b/fastlib/u/nvasil/tpie/ami_optimized_sort.h new file mode 100644 index 0000000000..388a74534f --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_optimized_sort.h @@ -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 + +#ifndef AMI_STREAM_IMP_SINGLE +# warning Including __FILE__ when AMI_STREAM_IMP_SINGLE undefined. +#endif + +#include +#include + +//------------------------------------------------------------ +template +AMI_err +AMI_optimized_sort(AMI_STREAM *instream, AMI_STREAM *outstream) { + + return AMI_partition_and_merge(instream, outstream); +} + +//------------------------------------------------------------ +template +AMI_err +AMI_optimized_sort(AMI_STREAM *instream, AMI_STREAM *outstream, + int keyoffset, KEY dummykey) { + + return AMI_partition_and_merge(instream, outstream, keyoffset, dummykey); +} + + +#endif diff --git a/fastlib/u/nvasil/tpie/ami_point.h b/fastlib/u/nvasil/tpie/ami_point.h new file mode 100644 index 0000000000..8908b1e9ff --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_point.h @@ -0,0 +1,440 @@ +// Copyright (C) 2002 Octavian Procopiuc +// +// File: ami_point.h +// Author: Octavian Procopiuc +// +// 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 + +// This is a hack. It works for integer types only. +template +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 +coord_t infinity_t::minf = (1 << (8*sizeof(coord_t) - 1)); +template +coord_t infinity_t::pinf = ~(1 << (8*sizeof(coord_t) - 1)); + +//int infinity_t::minf = (1 << (8*sizeof(int) - 1)); +//int infinity_t::pinf = ~(1 << (8*sizeof(int) - 1)); + +// The base class for AMI_point. +template +class AMI_point_base { +protected: + coord_t coords_[dim]; +public: + static infinity_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& 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& p) { + for (size_t j = 0; j < dim; j++) + coords_[j] = min(coords_[j], p[j]); + } + + void set_max(const AMI_point_base& 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& 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 +infinity_t AMI_point_base::Inf = infinity_t(); + + +// The AMI_point class. +template +class AMI_point: public AMI_point_base { + protected: + using AMI_point_base::coords_; + + public: + bool operator==(const AMI_point& 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& p1, + const AMI_point& 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& p1, + const AMI_point& p2) const { + return (compare(p1, p2) == -1); + } + + private: + int _compare(const AMI_point& p1, + const AMI_point& 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 +ostream& operator<<(ostream& s, const AMI_point& 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 AMI_point: public AMI_point_base { + protected: + using AMI_point_base::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& p) const { + return (coords_[0] == p.coords_[0]) && + (coords_[1] == p.coords_[1]); + } + bool operator!=(const AMI_point& p) const { + return (coords_[0] != p.coords_[0]) || + (coords_[1] != p.coords_[1]); + } + bool less_x(const AMI_point& b) const { + return (coords_[0] < b.coords_[0]) || + ((coords_[0] == b.coords_[0]) && (coords_[1] < b.coords_[1])); + } + bool less_y(const AMI_point& 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& a, + const AMI_point& b) const + { return (a[0] < b[0]) || ((a[0] == b[0]) && (a[1] < b[1])); } + }; + + struct less_Y { + bool operator()(const AMI_point& a, + const AMI_point& 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& p1, + const AMI_point& 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& p1, + const AMI_point& p2) const { + return (compare(p1, p2) == -1); + } + }; +}; + +template +ostream& operator<<(ostream& s, const AMI_point& p) { + return s << p[0] << " " << p[1]; +} +#endif // !_WIN32 + + +template +class AMI_record_base { +public: + typedef AMI_point 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& 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& 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& p) { + for (size_t j = 0; j < dim; j++) + key[j] = min(key[j], p[j]); + } + + void set_max(const AMI_record_base& 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& 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 AMI_record: public AMI_record_base { +public: + AMI_record(const typename AMI_record_base::point_t& p, data_t b = data_t(0)): + AMI_record_base(p, b) {} + AMI_record(data_t b = data_t(0)): AMI_record_base(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& p1, + const AMI_record& 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& p1, + const AMI_record& p2) const { + return (compare(p1, p2) == -1); + } + + private: + int _compare(const AMI_record& p1, + const AMI_record& 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 +ostream& operator<<(ostream& s, const AMI_record& 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 +struct AMI_record: public AMI_record_base { +public: + AMI_record(const typename AMI_record_base::point_t& p, data_t b = data_t(0)): + AMI_record_base(p, b) {} + AMI_record(const coord_t& x, const coord_t& y, const data_t& b): + AMI_record_base(point_t(x, y), b) {} + AMI_record(data_t b = data_t(0)): AMI_record_base(b) {} + + struct less_X_point { + bool operator()(const AMI_record& r, + const typename AMI_record_base::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& r1, + const AMI_record& r2) const { + // return point_t::less_X()(r1.key, r2.key); + return r1.key.less_x(r2.key); + } + + int compare(const AMI_record& r1, + const AMI_record& 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& r1, + const AMI_record& r2) const { + // return point_t::less_Y()(r1.key, r2.key); + return r1.key.less_y(r2.key); + } + + int compare(const AMI_record& r1, + const AMI_record& 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& p1, + const AMI_record& 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& p1, + const AMI_record& p2) const { + return (compare(p1, p2) == -1); + } + }; +}; + +template +ostream& operator<<(ostream& s, const AMI_record& p) { + return s << p[0] << " " << p[1] << " " << p.id(); +} +#endif // !_WIN32 + + +// Function object to extract the key from a record. +template +class AMI_record_key { +public: + AMI_point operator()(const AMI_record& r) const + { return r.key; } +}; + +#endif // AMI_POINT_H_ diff --git a/fastlib/u/nvasil/tpie/ami_queue.h b/fastlib/u/nvasil/tpie/ami_queue.h new file mode 100644 index 0000000000..3011e25992 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_queue.h @@ -0,0 +1,122 @@ +// Copyright (c) 2005 Andrew Danner +// +// File: ami_queue.h +// Author: Andrew Danner +// 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 +// Get the AMI_STREAM definition. +#include +#include + +// Basic Implementation of I/O Efficient FIFO queue. +// Uses two stacks +template +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* enQstack; + AMI_stack* deQstack; + TPIE_OS_OFFSET Qsize; +}; + +//Constructor for Temporary Queue +template +AMI_queue::AMI_queue() { + enQstack = new AMI_stack(); + deQstack = new AMI_stack(); + enQstack->persist(PERSIST_DELETE); + deQstack->persist(PERSIST_DELETE); + Qsize=0; +} + +//Constructor for Queue with filename +template +AMI_queue::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(fname); + strncpy(fname, basename, BTE_STREAM_PATH_NAME_LEN-4); + strcat(fname,".dq"); + deQstack = new AMI_stack(fname); + enQstack->persist(PERSIST_PERSISTENT); + deQstack->persist(PERSIST_PERSISTENT); + Qsize=enQstack->stream_len()+deQstack->stream_len(); +} + +template +AMI_queue::~AMI_queue(void) +{ + delete enQstack; + delete deQstack; +} + +template +void AMI_queue::persist(persistence p) { + enQstack->persist(p); + deQstack->persist(p); +} + +template +AMI_err AMI_queue::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 +AMI_err AMI_queue::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 diff --git a/fastlib/u/nvasil/tpie/ami_scan.h b/fastlib/u/nvasil/tpie/ami_scan.h new file mode 100644 index 0000000000..42a637fe80 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_scan.h @@ -0,0 +1,359 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: ami_scan.H +// Author: Darren Erik Vengroff +// 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 +#include + +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 +// 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 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 +AMI_err AMI_identity_scan::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 +AMI_err AMI_copy_stream(AMI_stream_base *t, AMI_stream_base *s) +{ + AMI_identity_scan id; + + return AMI_scan(t, &id, s); +} + +#endif // _AMI_SCAN_H + diff --git a/fastlib/u/nvasil/tpie/ami_scan.h.head b/fastlib/u/nvasil/tpie/ami_scan.h.head new file mode 100644 index 0000000000..425bbecb56 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_scan.h.head @@ -0,0 +1,34 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: ami_scan.h +// Author: Darren Erik Vengroff +// 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 +#include + +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. + + + diff --git a/fastlib/u/nvasil/tpie/ami_scan.h.tail b/fastlib/u/nvasil/tpie/ami_scan.h.tail new file mode 100644 index 0000000000..30f220c42a --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_scan.h.tail @@ -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 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 +AMI_err AMI_identity_scan::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 +AMI_err AMI_copy_stream(AMI_stream_base *t, AMI_stream_base *s) +{ + AMI_identity_scan id; + + return AMI_scan(t, &id, s); +} + +#endif // _AMI_SCAN_H + diff --git a/fastlib/u/nvasil/tpie/ami_scan_mac.cc b/fastlib/u/nvasil/tpie/ami_scan_mac.cc new file mode 100644 index 0000000000..7262366032 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_scan_mac.cc @@ -0,0 +1,17 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: ami_scan_mac.cpp +// Author: Darren Erik Vengroff +// 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" + + diff --git a/fastlib/u/nvasil/tpie/ami_scan_mac.h b/fastlib/u/nvasil/tpie/ami_scan_mac.h new file mode 100644 index 0000000000..c3cb63ada8 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_scan_mac.h @@ -0,0 +1,283 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: ami_scan_mac.h +// Author: Darren Erik Vengroff +// 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 diff --git a/fastlib/u/nvasil/tpie/ami_scan_utils.h b/fastlib/u/nvasil/tpie/ami_scan_utils.h new file mode 100644 index 0000000000..c68761cc5e --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_scan_utils.h @@ -0,0 +1,94 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: ami_scan_utils.h +// Author: Darren Erik Vengroff +// 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 + +// Get definitions for working with Unix and Windows. +#include +// Get the AMI_scan_object definition. +#include + +// 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 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 +cxx_istream_scan::cxx_istream_scan(istream *instr) : is(instr) +{ +}; + +template +AMI_err cxx_istream_scan::initialize(void) +{ + return AMI_ERROR_NO_ERROR; +}; + +template +AMI_err cxx_istream_scan::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 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 +cxx_ostream_scan::cxx_ostream_scan(ostream *outstr) : os(outstr) +{ +}; + +template +AMI_err cxx_ostream_scan::initialize(void) +{ + return AMI_ERROR_NO_ERROR; +}; + +template +AMI_err cxx_ostream_scan::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 diff --git a/fastlib/u/nvasil/tpie/ami_sort.h b/fastlib/u/nvasil/tpie/ami_sort.h new file mode 100644 index 0000000000..8e7dbf0f96 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_sort.h @@ -0,0 +1,24 @@ +// +// File: ami_sort.h +// Author: Darren Erik Vengroff +// 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 + +#define CONST const + +#include + +#ifdef AMI_STREAM_IMP_SINGLE +#include +#include +#include +#endif + +#endif // _AMI_SORT_H diff --git a/fastlib/u/nvasil/tpie/ami_sort_single.h b/fastlib/u/nvasil/tpie/ami_sort_single.h new file mode 100644 index 0000000000..b1e552bd1c --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_sort_single.h @@ -0,0 +1,467 @@ +// +// File: ami_sort_single.h +// Author: Darren Erik Vengroff +// 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 + +#ifndef AMI_STREAM_IMP_SINGLE +# warning Including __FILE__ when AMI_STREAM_IMP_SINGLE undefined. +#endif + +// For use in core by main_mem_operate(). +#include +#include + +#include +#include + + +// 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 merge_sort_manager /*: public AMI_merge_base */{ +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 +merge_sort_manager::merge_sort_manager(void) +{ +} + +template +merge_sort_manager::~merge_sort_manager(void) +{ + if (pq != NULL) { + delete pq; + } +} + + + +template +TPIE_OS_SIZE_T merge_sort_manager::space_usage_per_stream(void) +{ + return sizeof(arity_t) + sizeof(T); +} + +template +AMI_err merge_sort_manager::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 merge_sort_manager_op : public merge_sort_manager { +private: + Q *new_pqueue(arity_t arity); +protected: + using merge_sort_manager::pq; + using merge_sort_manager::input_arity; +#if DEBUG_ASSERTIONS + using merge_sort_manager::input_count; + using merge_sort_manager::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 +merge_sort_manager_op::merge_sort_manager_op(void) +{ + pq = NULL; +} + +template +Q *merge_sort_manager_op::new_pqueue(arity_t arity) +{ + return pq = new Q (arity); +} + +template +merge_sort_manager_op::~merge_sort_manager_op(void) +{ +} + +template +AMI_err merge_sort_manager_op::main_mem_operate(T* mm_stream, TPIE_OS_SIZE_T len) +{ + quick_sort_op(mm_stream, len); + return AMI_ERROR_NO_ERROR; +} + +template +TPIE_OS_SIZE_T merge_sort_manager_op::space_usage_overhead(void) +{ + return sizeof(Q); +} + +template +AMI_err merge_sort_manager_op::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 merge_sort_manager_obj : public merge_sort_manager { +private: + CMPR *cmp_o; + Q *new_pqueue(arity_t arity); +protected: + using merge_sort_manager::pq; + using merge_sort_manager::input_arity; +#if DEBUG_ASSERTIONS + using merge_sort_manager::input_count; + using merge_sort_manager::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 +merge_sort_manager_obj::merge_sort_manager_obj(CMPR *cmp) +{ + cmp_o = cmp; + pq = NULL; +} + +template +Q *merge_sort_manager_obj::new_pqueue(arity_t arity) +{ + return pq = new Q (arity,cmp_o); +} + +template +merge_sort_manager_obj::~merge_sort_manager_obj(void) +{ +} + + +template +AMI_err merge_sort_manager_obj::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 +TPIE_OS_SIZE_T merge_sort_manager_obj::space_usage_overhead(void) +{ + return sizeof(Q); +} + +template +AMI_err merge_sort_manager_obj::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 merge_sort_manager_cmp : public merge_sort_manager { +private: + int (*cmp_f)(CONST T&, CONST T&); + Q *new_pqueue(arity_t arity); +protected: + using merge_sort_manager::pq; + using merge_sort_manager::input_arity; +#if DEBUG_ASSERTIONS + using merge_sort_manager::input_count; + using merge_sort_manager::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 +merge_sort_manager_cmp::merge_sort_manager_cmp(int (*cmp)(CONST T&, + CONST T&)) +{ + cmp_f = cmp; + pq = NULL; +} + +template +Q *merge_sort_manager_cmp::new_pqueue(arity_t arity) +{ + return pq = new Q (arity,cmp_f); +} + + +template +merge_sort_manager_cmp::~merge_sort_manager_cmp(void) +{ +} + + +template +AMI_err merge_sort_manager_cmp::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 +TPIE_OS_SIZE_T merge_sort_manager_cmp::space_usage_overhead(void) +{ + return sizeof(Q); +} + +template +AMI_err merge_sort_manager_cmp::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 +AMI_err AMI_sort_V1(AMI_STREAM *instream, AMI_STREAM *outstream, + int (*cmp)(CONST T&, CONST T&)) +{ + merge_sort_manager_cmp > msm(cmp); + + return AMI_generalized_partition_and_merge(instream, outstream, + (merge_sort_manager_cmp > *)&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 +AMI_err AMI_sort_V1(AMI_STREAM *instream, AMI_STREAM *outstream) +{ + merge_sort_manager_op > msm; + + return AMI_generalized_partition_and_merge(instream, outstream, + (merge_sort_manager_op > *)&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 +AMI_err AMI_sort_V1(AMI_STREAM *instream, AMI_STREAM *outstream, + CMPR *cmp) +{ + merge_sort_manager_obj,CMPR > msm(cmp); + + return AMI_generalized_partition_and_merge (instream, outstream, + (merge_sort_manager_obj,CMPR> *)&msm); +} + +#endif // _AMI_SORT_SINGLE_H diff --git a/fastlib/u/nvasil/tpie/ami_sort_single_dh.h b/fastlib/u/nvasil/tpie/ami_sort_single_dh.h new file mode 100644 index 0000000000..2980a93ab0 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_sort_single_dh.h @@ -0,0 +1,1113 @@ +// +// File: ami_sort_single_dh.h +// +// $Id: ami_sort_single_dh.h,v 1.18 2005/08/24 19:32:38 adanner Exp $ +// +// This file contains the templated routines +// 1) AMI_sort: +// a) AMI_err AMI_sort(AMI_STREAM *instream, +// AMI_STREAM *outstream) +// b) AMI_err AMI_sort(AMI_STREAM *instream, +// AMI_STREAM *outstream, +// CMPR *cmp) +// 2) AMI_ptr_sort +// a) AMI_err AMI_ptr_sort(AMI_STREAM *instream, +// AMI_STREAM *outstream) +// b) AMI_err AMI_ptr_sort(AMI_STREAM *instream, +// AMI_STREAM *outstream, +// CMPR *cmp) +// 3) AMI_key_sort +// a) AMI_err AMI_key_sort(AMI_STREAM *instream, +// AMI_STREAM *outstream, +// KEY dummykey, +// CMPR *cmp) +// +// and the supporting class +// sort_manager : a base class for sort managers +// to actually sort, given an internal sort implementation and +// a single merge implementation +// + +// The routines AMI_partition_and_merge and AMI_single_merge are also +// used, and can be found in file apm_dh.h. Besides the sort manager +// class, the sort routines are parameterized by an appropriate "merge +// heap class" and instantiate an object of that type. These classes +// can be found in file mergeheap_dh.h. + + +#ifndef _AMI_SORT_SINGLE_DH_H +#define _AMI_SORT_SINGLE_DH_H + +// Get definitions for working with Unix and Windows +#include +#include +#include +#include //For templated heaps +#include // Contains classes for sorting internal runs + // using different comparison types +#include //for log, ceil, etc. + +#ifndef AMI_STREAM_IMP_SINGLE +#warning Including __FILE__ when AMI_STREAM_IMP_SINGLE undefined. +#endif + +typedef int arity_t; + +// A class of merge objects for merge sorting objects of type T. We +// will actually use one of two subclasses of this class which use +// either a comparison object, or the binary comparison operator <. + +template +class sort_manager{ + private: + AMI_STREAM* inStream; + AMI_STREAM* outStream; + AMI_err ae; //For catching error codes + TPIE_OS_OFFSET nInputItems; //Number of items in inStream; + TPIE_OS_OFFSET mmBytesAvail; //Amount of spare memory we can use + TPIE_OS_SIZE_T mmBytesPerStream; //Memory consumed by each Stream obj + + bool bProgress; //flag indicating if we show progress bar + TPIE_OS_OFFSET progCount; + + bool use2xSpace; //flag to indicate if we are doing a 2x sort + + // The maximum number of stream items of type T that we can + // sort in internal memory + TPIE_OS_OFFSET nItemsPerRun; + + TPIE_OS_OFFSET nRuns; //The number of sorted runs left to merge + arity_t mrgArity; //Max runs we can merge at one time + + // The output stream to which we are currently writing runs + AMI_STREAM* curOutputRunStream; + + // The mininum number of runs in each output stream + // some streams can have one additional run + TPIE_OS_OFFSET minRunsPerStream; + // The number of extra runs or the number of streams that + // get one additional run. + arity_t nXtraRuns; + + // The last run can have fewer than nItemsPerRun; + TPIE_OS_OFFSET nItemsInLastRun; + // How many items we will sort in a given run + TPIE_OS_OFFSET nItemsInThisRun; + // For each output stream, how many runs it should get + TPIE_OS_OFFSET runsInStream; + + // A suffix to use in forming output file names. During the merge phase + // we keep two sets of files, the input files and the output files to + // which we are merging. The input file suffix is the opposite of the + // output file suffix. After merging one level, the output streams + // become the input for the next level. + char *suffixName[2]; + // A buffer for building the output file names + char newName [BTE_STREAM_PATH_NAME_LEN]; + //prefix of temp files created during sort + char *working_disk; + + AMI_err start_sort(); //high level wrapper to full sort + AMI_err compute_sort_params(); //compute nInputItems, mrgArity, nRuns + AMI_err partition_and_sort_runs(); //make initial sorted runs + AMI_err merge_to_output(); //loop over merge tree, create output stream + // Merge a single group mrgArity streams to an output stream + AMI_err single_merge(AMI_STREAM**, arity_t, AMI_STREAM*, + TPIE_OS_OFFSET); + + //helper function for creating filename + inline void make_name(char *prepre, char *pre, int id, char *dest); + + public: + sort_manager(I isort, M mheap); + ~sort_manager(){}; + I InternalSorter; + M MergeHeap; + //A version that uses 3x space and saves input stream + AMI_err sort(AMI_STREAM* in, AMI_STREAM* out, bool progress); + //A version that uses 2x space and overwrites input stream + AMI_err sort(AMI_STREAM* in, bool progress); +}; + +template +sort_manager::sort_manager(I isort, M mheap): + InternalSorter(isort), MergeHeap(mheap) +{ + suffixName[0]="_0_"; + suffixName[1]="_1_"; + //prefix of temp files created during sort + working_disk = tpie_tempnam("AMI"); +}; + +template +AMI_err sort_manager::sort(AMI_STREAM* in, AMI_STREAM* out, + bool progress=false){ + + //This version saves the original input and uses 3x space + //(input, current temp runs, output runs) + + bProgress=progress; + inStream=in; + outStream=out; + use2xSpace=false; + // Basic checks that input is ok + if(inStream==NULL || outStream==NULL) { return AMI_ERROR_NULL_POINTER;} + if(!inStream || !outStream) { return AMI_ERROR_OBJECT_INVALID; } + if(inStream->stream_len() < 2) { return AMI_SORT_ALREADY_SORTED; } + + // Else, there is something to sort, do it + return start_sort(); +} + +template +AMI_err sort_manager::sort(AMI_STREAM* in, bool progress=false){ + + //This version overwrites the original input and uses 2x space + //The input stream is truncated to length 0 after forming initial runs + //and only two levels of the merge tree are on disk at any one time. + bProgress=progress; + inStream=in; + outStream=in; //output destination is same as input + use2xSpace=true; + // Basic checks that input is ok + if(inStream==NULL) { return AMI_ERROR_NULL_POINTER;} + if(!inStream) { return AMI_ERROR_OBJECT_INVALID; } + if(inStream->stream_len() < 2) { return AMI_SORT_ALREADY_SORTED; } + + // Else, there is something to sort, do it + return start_sort(); +} + +template +AMI_err sort_manager::start_sort(){ + + TP_LOG_DEBUG_ID ("sort_manager::sort START"); + if(bProgress){ cout << "\n----Starting TPIE Sort----" << endl; } + // ******************************************************************** + // * PHASE 1: See if we can sort the entire stream in internal memory * + // * without the need to use general merge sort * + // ******************************************************************** + + + // Figure out how much memory we've got to work with. + mmBytesAvail = MM_manager.memory_available(); + + // Space for internal buffers for the input and output stream may not + // have been allocated yet. Query the space usage and subtract. + if ((ae = inStream->main_memory_usage + (&mmBytesPerStream,MM_STREAM_USAGE_MAXIMUM)) + != AMI_ERROR_NO_ERROR) { + TP_LOG_DEBUG_ID ("Error returned from main_memory_usage"); + return ae; + } + + TP_LOG_DEBUG_ID ("BTE says we use at most "<< mmBytesPerStream + << "bytes per stream"); + + // This is how much we can use for internal sort if + // we are not doing general merge sort + mmBytesAvail -= 2 * mmBytesPerStream; + + // Check if all input items can be sorted internally using less than + // mmBytesAvail + nInputItems = inStream->stream_len(); + inStream->seek (0); + if (nInputItems +AMI_err sort_manager::compute_sort_params(void){ + // ******************************************************************** + // * PHASE 2: Compute/check limits * + // * Compute the maximum number of items we can sort in main memory * + // * and the maximium number of sorted runs we can merge at one time * + // * Before doing any sorting, check that we can fit at least one item* + // * in internal memory for sorting and that we can merge at least two* + // * runs at at time * + // * * + // * Memory needed for the run formation phase: * + // * 2*mmBytesPerStream + {for input/output streams} * + // * nItemsPerRun*space_per_sort_item() + {for each item sorted } * + // * space_overhead_sort() {constant overhead in * + // * sort management object * + // * during sorting } * + // * * + // * Memory needed for a D-way merge: * + // * Cost per merge stream: * + // * mmBytesPerStream+ {a open stream to read from} * + // * space_per_merge_item()+ {used in internal merge heap} * + // * sizeof(T*)+sizeof(off_t) {arrays in single_merge()} * + // * sizeof(AMI_STREAM*) {array element that points to * + // * merge stream} * + // * Fixed costs: * + // * 2*mmBytesPerStream+ {original input stream + output * + // * of current merge} * + // * space_overhead_merge()+ {fixed dynamic memory costs of * + // * merge heap} * + // * 3*space_overhead() {overhead per "new" memory request * + // * for allocating 2 arrays of streams* + // * and two arrays in single_merge} * + // * * + // * Total cost for D-way Merge: * + // * D*(Cost per merge stream)+(Fixed costs) * + // * * + // * Any additional memory requests that call "new" directly or * + // * indirectly should be documented and accounted for in this phase * + // ******************************************************************** + + TP_LOG_DEBUG_ID ("Computing merge sort parameters."); + + TPIE_OS_OFFSET mmBytesAvailSort; // Bytes available for sorting + + TP_LOG_DEBUG_ID ("Each object of size " << sizeof(T) << " uses " + << InternalSorter.space_per_item () << " bytes " + << "for sorting in memory"); + + //Subtract off size of temp output stream + //The size of the input stream was already subtracted from + //mmBytesAvail + mmBytesAvailSort=mmBytesAvail - mmBytesPerStream; + + nItemsPerRun=InternalSorter.MaxItemCount(mmBytesAvailSort); + + if(nItemsPerRun<1){ + TP_LOG_FATAL_ID ("Insufficient Memory for forming sorted runs"); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + + // Now we know the max number of Items we can sort in a single + // internal memory run. Next, compute the number of runs we can + // merge together at one time + + TPIE_OS_SIZE_T mmBytesPerMergeItem = mmBytesPerStream + + MergeHeap.space_per_item() + sizeof(T*) + + sizeof(TPIE_OS_OFFSET)+sizeof(AMI_STREAM*); + + // Fixed cost of mergheap impl. + MM_manager overhead of allocating + // an array of AMI_STREAM ptrs (pending) + // cost of Input stream already accounted for in mmBytesAvail.. + TPIE_OS_SIZE_T mmBytesFixedForMerge = MergeHeap.space_overhead() + + mmBytesPerStream + 3*MM_manager.space_overhead(); + + TPIE_OS_OFFSET mmBytesAvailMerge = mmBytesAvail - mmBytesFixedForMerge; + // Need to support at least binary merge + if(mmBytesAvailMerge<2*mmBytesPerMergeItem){ + TP_LOG_FATAL_ID ("Merge arity < 2 -- Insufficient memory for a merge."); + return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY; + } + + // Cast down from TPIE_OS_OFFSET (type of mmBytesAvail). + // mmBytesPerMergeItem is at least 1KB, so we are OK unless we + // have more than 2 TerraBytes of memory. I look forward to the day + // this comment seems silly and wrong + mrgArity = + (arity_t)(mmBytesAvail-mmBytesFixedForMerge)/mmBytesPerMergeItem; + TP_LOG_DEBUG_ID("mem avail=" << mmBytesAvail-mmBytesFixedForMerge + << " bytes per merge item=" << mmBytesPerMergeItem + << " initial mrgArity=" << mrgArity); + + // 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, max number of file descriptors, etc. + int availableStreams = inStream->available_streams (); + + // Merging requires an available streams/file decriptor for + // each of the mrgArity input. We need one additional file descriptor + // for the output of the current merge, so binary merge requires + // three available streams. + if (availableStreams < 3) { + TP_LOG_FATAL_ID ("Not enough stream descriptors available " << + "to perform merge."); + return AMI_ERROR_INSUFFICIENT_AVAILABLE_STREAMS; + } + + // Can at least do binary merge. See if availableStreams limits + // maximum mrgArity + if (mrgArity > availableStreams - 1) { + mrgArity = (availableStreams - 1); + TP_LOG_WARNING_ID ("Reduced merge arity due to AMI restrictions."); + } + + // The number of memory-sized runs that the original input stream + // will be partitioned into. + nRuns = ((nInputItems + nItemsPerRun - 1) / + nItemsPerRun); + +#ifdef TPIE_SORT_SMALL_MRGARITY + // KEEP OUT!!! + // This should not be done by the typical user and is only for + // testing/debugging purposes. ONLY define this flag and set a value + // if you know what you are doing. + TP_LOG_WARNING_ID("Reducing merge arity due to compiler specified flag"); + if(mrgArity > TPIE_SORT_SMALL_MRGARITY) { + mrgArity=TPIE_SORT_SMALL_MRGARITY; + } +#endif // TPIE_SORT_SMALL_MRGARITY + +#ifdef TPIE_SORT_SMALL_RUNSIZE + // KEEP OUT!!! + // This should not be done by the typical user and is only for + // testing/debugging purposes ONLY define this flag and set a value + // if you know what you are doing. + TP_LOG_WARNING_ID("Reducing run size due to compiler specified flag"); + if(nItemsPerRun > TPIE_SORT_SMALL_RUNSIZE) { + nItemsPerRun=TPIE_SORT_SMALL_RUNSIZE; + } + + // need to adjust nRuns + nRuns = ((nInputItems + nItemsPerRun - 1) / nItemsPerRun); +#endif // TPIE_SORT_SMALL_RUNSIZE + + //#define MINIMIZE_INITIAL_RUN_LENGTH +#ifdef MINIMIZE_INITIAL_RUN_LENGTH + // If compiled with the above flag, try to reduce the length of + // the initial sorted runs without increasing the merge tree height + // This could be a speed-up if it is faster to quicksort many small + // runs + // and merge many small runs than it is to quicksort fewer long runs + // and + // merge them. + TP_LOG_DEBUG_ID ("Minimizing initial run lengths without increasing" << + "the height of the merge tree."); + + // The tree height is the ceiling of the log base mrgArity of the + // number of original runs. + double tree_height = log((double)nRuns) / log((double)mrgArity); + tp_assert (tree_height > 0, "Negative or zero tree height!"); + tree_height = ceil (tree_height); + + // See how many runs we could possibly fit in the tree without + // increasing the height. + double maxOrigRuns = pow ((double) mrgArity, tree_height); + tp_assert (maxOrigRuns >= nRuns, + "Number of permitted runs was reduced."); + + // How big will such runs be? + double new_nItemsPerRun = ceil (nInputItems/ maxOrigRuns); + tp_assert (new_nItemsPerRun <= nItemsPerRun, + "Size of original runs increased."); + + // Update the number of items per run and the number of original runs + nItemsPerRun = (TPIE_OS_SIZE_T) new_nItemsPerRun; + + TP_LOG_DEBUG_ID ("With long internal memory runs, nRuns = " + << nRuns << '\n'); + + nRuns = (nInputItems + nItemsPerRun - 1) / nItemsPerRun; + + TP_LOG_DEBUG_ID ("With shorter internal memory runs " + << "and the same merge tree height, nRuns = " + << nRuns << '\n'); + + tp_assert (maxOrigRuns >= nRuns, + "We increased the merge height when we weren't supposed to do so."); +#endif // MINIMIZE_INITIAL_SUBSTREAM_LENGTH + + + // If we have just a few runs, we don't need the + // full mrgArity. This is the last change to mrgArity + if(mrgArity>nRuns){mrgArity=nRuns;} + + // We should always end up with at least two runs + // otherwise why are we doing it externally? + tp_assert (nRuns > 1, "Less than two runs to merge!"); + // Check that numbers are consistent with input size + tp_assert (nRuns * nItemsPerRun - nInputItems < nItemsPerRun, + "Total expected output size is too large."); + tp_assert (nInputItems - (nRuns - 1) * nItemsPerRun <= nItemsPerRun, + "Total expected output size is too small."); + + if(bProgress){ + cout << "Input stream has " << nInputItems << " Items\n" + << "Forming " << nRuns << " initial runs of at most " + << nItemsPerRun << " items each\n" + << "Merge arity is " << mrgArity << endl; + } + + TP_LOG_DEBUG_ID ("Input stream has " << nInputItems << " Items"); + TP_LOG_DEBUG_ID ("Max number of items per runs " << nItemsPerRun ); + TP_LOG_DEBUG_ID ("Initial number of runs " << nRuns ); + TP_LOG_DEBUG_ID ("Merge arity is " << mrgArity ); + + return AMI_ERROR_NO_ERROR; +} +template +AMI_err sort_manager::partition_and_sort_runs(void){ + // ******************************************************************** + // * PHASE 3: Partition * + // * Partition the input stream into nRuns of at most nItemsPerRun * + // * and sort them, and write them to temporay output files. * + // * The last run may have fewer than nItemsPerRun. To keep the number* + // * of files down and to support sequential I/O, we distribute the * + // * nRuns evenly across mrgArity files, thus each file on disk holds * + // * multiple sorted runs. * + // ******************************************************************** + + // The mininum number of runs in each output stream + // some streams can have one additional run + minRunsPerStream = nRuns/mrgArity; + // The number of extra runs or the number of streams that + // get one additional run. This is less than mrgArity and + // it is OK to downcast to an arity_t. + nXtraRuns = (arity_t) (nRuns - minRunsPerStream*mrgArity); + tp_assert(nXtraRunsseek(0); + + // ******************************************************************** + // * Partition and make initial sorted runs * + // ******************************************************************** + TPIE_OS_OFFSET check_size = 0; //for debugging + progCount=0; //for progress indication + for( ii=0; ii(newName); + // How many runs should this stream get? + // extra runs go in the LAST nXtraRuns streams so that + // the one short run is always in the LAST output stream + runsInStream = minRunsPerStream + ((ii >= mrgArity-nXtraRuns)?1:0); + for( jj=0; jj < runsInStream; jj++ ) { // For each run in this stream + // See if this is the last run + if( (ii==mrgArity-1) && (jj==runsInStream-1)) { + nItemsInThisRun=nItemsInLastRun; + } + // Sort it + if(bProgress){ + progCount++; + cout << "\rForming sorted run " << progCount << " of " << nRuns + << " [" << setw(6) << setiosflags(ios::fixed) + << setprecision(2) + << ((1.*progCount)/nRuns)*100. << "%]" << flush; + } + if ((ae = InternalSorter.sort(inStream, curOutputRunStream, + nItemsInThisRun))!= AMI_ERROR_NO_ERROR) + { + TP_LOG_FATAL_ID ("main_mem_operate failed"); + return ae; + } + } // For each run in this stream + // All runs created for this stream, clean up + TP_LOG_DEBUG_ID ("Wrote " << runsInStream << " runs and " + << curOutputRunStream->stream_len() << " items to file " << ii); + check_size+=curOutputRunStream->stream_len(); + curOutputRunStream->persist(PERSIST_PERSISTENT); + delete curOutputRunStream; + }//For each output stream + + tp_assert(check_size == nInputItems, "item count mismatch"); + + // Done with partitioning and initial run formation + // free space associated with internal memory sorting + InternalSorter.deallocate(); + if(bProgress){ cout << endl; } //newline + if(use2xSpace){ + //recall outStream/inStream point to same file in this case + inStream->truncate(0); //free up disk space + inStream->seek(0); + } + return AMI_ERROR_NO_ERROR; +} + +template +AMI_err sort_manager::merge_to_output(void){ + + // ******************************************************************** + // * PHASE 4: Merge * + // * Loop over all levels of the merge tree, reading mrgArity runs * + // * at a time from the streams at the current level and distributing * + // * merged runs over mrgArity output streams one level up, until * + // * a single output stream exists * + // ******************************************************************** + + // The input streams we from which will read sorted runs + AMI_STREAM **mergeInputStreams = new AMI_STREAM*[mrgArity]; + + //This mesage does not count space overhead per "new" + //should it? + TP_LOG_DEBUG_ID("Allocated " << sizeof(AMI_STREAM*)*mrgArity + << " bytes for " << mrgArity << " merge input stream pointers." + << " Mem. avail. is " << MM_manager.memory_available () ); + + // the number of iterations the main loop has gone through, + // the height of the merge tree log_{M/B}(N/B), typically 1 or 2 + int mrgHeight = 0; + int treeHeight; //for progress + TPIE_OS_OFFSET ii,jj; //index vars + + MergeHeap.allocate( mrgArity ); //Allocate mem for mergeheap + + // ***************************************************************** + // * * + // * 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. The final merge pass is handled outside the loop. * + // * Future extension may want to do something special in the last * + // * merge * + // * * + // ***************************************************************** + + if(bProgress){ + //compute merge depth, number of passes over data + treeHeight=(int)ceil(log((double)nRuns)/log((double)mrgArity)); + } + + while (nRuns > mrgArity){ + if(bProgress){ + progCount=0; + cout << "\rMerge pass " << mrgHeight+1 << " of " << treeHeight + << " [ 0.00\%]" << flush; + } + // We are not yet at the top of the merge tree + // Write merged runs to temporary output streams + TP_LOG_DEBUG_ID ("Intermediate merge. level="<0) ? nXtraRuns : mrgArity; + + // The number of extra runs or the number of streams that + // get one additional run. This is less than mrgArity and + // it is OK to downcast to an arity_t. + nXtraRuns = (arity_t) (nRuns - minRunsPerStream*mrgArity); + tp_assert(nXtraRuns 0) ? mrgArity : nXtraRuns; + + arity_t nRunsToMerge = mrgArity; // may change for last output run + + // is current merge output the last run on this merge level? + bool lastOutputRun = false; + + // open the mrgArity Input streams from which to read runs + for(ii = 0; ii < mrgArity; ii++){ + // Make the input file name + make_name(working_disk, suffixName[mrgHeight%2], ii, newName); + // Dynamically allocate the stream + // We account for these mmBytesPerStream in phase 2 + // (input stream to read from) + mergeInputStreams[ii] = new AMI_STREAM(newName); + mergeInputStreams[ii]->seek(0); + } + + TPIE_OS_OFFSET check_size=0; + // For each new output stream, fill with merged runs. + // strange indexing is that so if there are fewer than mrgArity + // output streams needed, we use the LAST nOutputStreams. This + // always keeps the one possible short run in the LAST of the + // mrgArity output streams. + TP_LOG_DEBUG_ID ("Writing " << nRuns << " runs to " << nOutputStreams + << " output files.\nEach output file has at least " + << minRunsPerStream << " runs."); + + for(ii = mrgArity-nOutputStreams; ii < mrgArity; ii++){ + // Make the output file name + make_name(working_disk, suffixName[(mrgHeight+1)%2], ii, newName); + // Dynamically allocate the stream + // We account for these mmBytesPerStream in phase 2 + // (temp merge output stream) + curOutputRunStream = new AMI_STREAM(newName); + + // How many runs should this stream get? + // extra runs go in the LAST nXtraRuns streams so that + // the one short run is always in the LAST output stream + runsInStream = minRunsPerStream + ((ii >= mrgArity-nXtraRuns)?1:0); + TP_LOG_DEBUG_ID ("Writing " << runsInStream << " runs to output " + << " file " << ii); + for( jj=0; jj < runsInStream; jj++ ) { // For each run in this stream + // See if this is the last run. + if( (ii==mrgArity-1) && (jj==runsInStream-1)) { + lastOutputRun=true; + nRunsToMerge=mergeRunsInLastOutputRun; + } + // Merge runs to curOutputRunStream + ae = single_merge(mergeInputStreams+mrgArity-nRunsToMerge, + nRunsToMerge, curOutputRunStream, nItemsPerRun); + if (ae != AMI_ERROR_NO_ERROR) { + TP_LOG_FATAL_ID("AMI_single_merge error"<< ae <<" in deep merge"); + return ae; + } + } // For each run in this stream + + // Commit new output stream to disk + TP_LOG_DEBUG_ID ("Wrote " << runsInStream << " runs and " + << curOutputRunStream->stream_len() << " items to file " << ii); + check_size+=curOutputRunStream->stream_len(); + curOutputRunStream->persist(PERSIST_PERSISTENT); + delete curOutputRunStream; + } // For each new output stream + + tp_assert(check_size==nInputItems, "item count mismatch in merge"); + // All output streams created/filled. + // Clean up, go up to next level + + // Delete temp input merge streams + for(ii = 0; ii < mrgArity; ii++){ + mergeInputStreams[ii]->persist(PERSIST_DELETE); + delete mergeInputStreams[ii]; + } + // Update run lengths + nItemsPerRun=mrgArity*nItemsPerRun; //except for maybe last run + mrgHeight++; // moving up a level + } // while (nRuns > mrgArity) + + tp_assert( nRuns > 1, "Not enough runs to merge to final output"); + tp_assert( nRuns <= mrgArity, "Too many runs to merge to final output"); + + // We are at the last merge phase, write to specified output stream + // Open up the nRuns final merge streams to merge + // These runs are packed in the LAST nRuns elements of the array + TP_LOG_DEBUG_ID ("Final merge. level="<(newName); + mergeInputStreams[ii-(mrgArity-nRuns)]->seek(0); + } + + if(bProgress){ + progCount=0; + cout << "\rFinal merge pass (" << mrgHeight+1 << " of " << treeHeight + << ") [ 0.00\%]" << flush; + } + // Merge last remaining runs to the output stream. + // mergeInputStreams is address( address (the first input stream) ) + ae = single_merge (mergeInputStreams, nRuns, outStream ); + + tp_assert(outStream->stream_len() == nInputItems, "item count mismatch"); + + if (ae != AMI_ERROR_NO_ERROR) { + TP_LOG_FATAL_ID ("AMI_ERROR " << ae << " returned by single_merge " + << "in final merge phase"); + return ae; + } + + TP_LOG_DEBUG_ID ("merge cleanup"); + if(bProgress){cout << endl;} //print newline + // We are done, except for cleanup. Is anyone still reading this? + // Delete temp input merge streams + for(ii = 0; ii < nRuns; ii++){ + mergeInputStreams[ii]->persist(PERSIST_DELETE); + delete mergeInputStreams[ii]; + } + // Delete stream ptr arrays + delete [] mergeInputStreams; + // Deallocate the merge heap, free up memory + MergeHeap.deallocate(); + TP_LOG_DEBUG_ID ("Number of passes incl run formation is " << + mrgHeight+2 ); + TP_LOG_DEBUG_ID ("AMI_partition_and_merge END"); + return AMI_ERROR_NO_ERROR; +} + +template +AMI_err sort_manager::single_merge( AMI_STREAM < T > **inStreams, + arity_t arity, AMI_STREAM < T >*outStream, TPIE_OS_OFFSET cutoff=-1 ) +{ + arity_t i; + AMI_err ami_err; + + TPIE_OS_OFFSET* nread = new TPIE_OS_OFFSET[arity]; + TPIE_OS_OFFSET progStep, progTarget; //for progress bar + + //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; + delete[] nread; + 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. * + // ********************************************************* + if(bProgress){ + progStep=(TPIE_OS_OFFSET)(0.0001*nInputItems); + progTarget=progCount+progStep; + } + 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; + delete[] nread; + 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; + delete[] nread; + 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]); + if(bProgress){ + progCount++; + if(progCount>progTarget){ + progTarget=progCount+progStep; + cout << "\b\b\b\b\b\b\b\b\b" << "[" << setw(6) + << setiosflags(ios::fixed) << setprecision(2) + << ((1.*progCount)/nInputItems)*100. << "%]" + << flush; + } + } + } + }//while + + //cleanup + delete [] in_objects; + delete [] nread; + if(bProgress){ + cout << "\b\b\b\b\b\b\b\b\b" << "[" << setw(6) << setiosflags(ios::fixed) + << setprecision(2) << ((1.*progCount)/nInputItems)*100. << "%]" + << flush; + } + return AMI_ERROR_NO_ERROR; +} + + +template +inline void sort_manager::make_name(char *prepre, char *pre, + int id, char *dest) +{ + //This buffer must be long enough to hold the + //largest possible stream id (in decimal) + //largest ID is at most mrgArity + char tmparray[6]; + + strcpy (dest, prepre); + strcat (dest, pre); + sprintf (tmparray, "%d", id); + strcat (dest, tmparray); +} + +// ******************************************************************* +// * * +// * The actual AMI_sort calls * +// * * +// ******************************************************************* + +// A version of AMI_sort that takes an input stream of elements of type +// T, and an output stream, and and uses the < operator to sort +template +AMI_err AMI_sort(AMI_STREAM *instream, AMI_STREAM *outstream, + bool progress=false) +{ + return sort_manager< T, Internal_Sorter_Op, merge_heap_dh_op > + (Internal_Sorter_Op(), merge_heap_dh_op() ).sort + (instream, outstream, progress); +} + +// 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 +AMI_err AMI_sort(AMI_STREAM *instream, AMI_STREAM *outstream, + CMPR *cmp, bool progress=false) +{ + return sort_manager, + merge_heap_dh_obj >( Internal_Sorter_Obj(cmp), + merge_heap_dh_obj(cmp) ).sort + (instream, outstream, progress); +} + +// ******************************************************************** +// * * +// * These are the versions that keep a heap of pointers to records * +// * * +// ******************************************************************** +// A version of AMI_sort that takes an input stream of elements of type +// T, and an output stream, and and uses the < operator to sort + +template +AMI_err AMI_ptr_sort(AMI_STREAM *instream, AMI_STREAM *outstream, + bool progress=false) +{ + return sort_manager< T, Internal_Sorter_Op, merge_heap_pdh_op > + (Internal_Sorter_Op(), merge_heap_pdh_op()).sort + (instream, outstream, progress); +} + +// 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 +AMI_err AMI_ptr_sort(AMI_STREAM *instream, AMI_STREAM *outstream, + CMPR *cmp, bool progress=false) +{ + return sort_manager, + merge_heap_pdh_obj >( Internal_Sorter_Obj(cmp), + merge_heap_pdh_obj(cmp) ).sort + (instream, outstream, progress); +} + +// ******************************************************************** +// * * +// * This version keeps a heap of keys to records * +// * * +// ******************************************************************** +// A version of AMI_sort that takes an input stream of elements of +// type T, an output stream, a key specification, and a user-specified +// comparison object. + +// The key specification consists of an example key, which is used to +// infer the type of the key field. 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, 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). + +template +AMI_err AMI_key_sort(AMI_STREAM *instream, AMI_STREAM *outstream, + KEY dummykey, CMPR *cmp, bool progress=false) +{ + return sort_manager, + merge_heap_dh_kobj >( Internal_Sorter_KObj(cmp), + merge_heap_dh_kobj(cmp) ).sort + (instream, outstream, progress); +} + +// ******************************************************************** +// * * +// * Duplicates of the above versions that only use 2x space and * +// * overwrite the original input stream * +// * * +// ******************************************************************** + +// A version of AMI_sort that takes an input stream of elements of type +// T, and and uses the < operator to sort +template +AMI_err AMI_sort(AMI_STREAM *instream, bool progress=false) +{ + return sort_manager< T, Internal_Sorter_Op, merge_heap_dh_op > + (Internal_Sorter_Op(), merge_heap_dh_op() ).sort + (instream, progress); +} + +// A version of AMI_sort that takes an input stream of elements of +// type T, 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 +AMI_err AMI_sort(AMI_STREAM *instream, CMPR *cmp, bool progress=false) +{ + return sort_manager, + merge_heap_dh_obj >( Internal_Sorter_Obj(cmp), + merge_heap_dh_obj(cmp) ).sort + (instream, progress); +} + +// ******************************************************************** +// * * +// * These are the versions that keep a heap of pointers to records * +// * * +// ******************************************************************** +// A version of AMI_sort that takes an input stream of elements of type +// T, and and uses the < operator to sort + +template +AMI_err AMI_ptr_sort(AMI_STREAM *instream, bool progress=false) +{ + return sort_manager< T, Internal_Sorter_Op, merge_heap_pdh_op > + (Internal_Sorter_Op(), merge_heap_pdh_op()).sort + (instream, progress); +} + +// A version of AMI_sort that takes an input stream of elements of +// type T, 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 +AMI_err AMI_ptr_sort(AMI_STREAM *instream, CMPR *cmp, bool progress=false) +{ + return sort_manager, + merge_heap_pdh_obj >( Internal_Sorter_Obj(cmp), + merge_heap_pdh_obj(cmp) ).sort + (instream, progress); +} + +// ******************************************************************** +// * * +// * This version keeps a heap of keys to records * +// * * +// ******************************************************************** +// A version of AMI_sort that takes an input stream of elements of +// type T, a key specification, and a user-specified +// comparison object. + +// The key specification consists of an example key, which is used to +// infer the type of the key field. 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, 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). + +template +AMI_err AMI_key_sort(AMI_STREAM *instream, KEY dummykey, CMPR *cmp, + bool progress=false) +{ + return sort_manager, + merge_heap_dh_kobj >( Internal_Sorter_KObj(cmp), + merge_heap_dh_kobj(cmp) ).sort + (instream, progress); +} + +/* +DEPRECATED: comparison function sorting +Earlier TPIE versions allowed a sort 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 + +Sincerely, +the management +*/ + +#endif // _AMI_SORT_SINGLE_DH_H diff --git a/fastlib/u/nvasil/tpie/ami_sparse_matrix.h b/fastlib/u/nvasil/tpie/ami_sparse_matrix.h new file mode 100644 index 0000000000..6307961d61 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_sparse_matrix.h @@ -0,0 +1,376 @@ +// Copyright (c) 1995 Darren Vengroff +// +// File: ami_sparse_matrix.h +// Author: Darren Vengroff +// 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 + +#include + +// We need dense matrices to support some sparse/dense interactions. +#include + +// 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 AMI_sm_elem { +public: + TPIE_OS_OFFSET er; + TPIE_OS_OFFSET ec; + T val; +}; + + +template +ostream &operator<<(ostream& s, const AMI_sm_elem &a) +{ + return s << a.er << ' ' << a.ec << ' ' << a.val; +}; + +template +istream &operator>>(istream& s, AMI_sm_elem &a) +{ + return s >> a.er >> a.ec >> a.val; +}; + + +template +class AMI_sparse_matrix : public AMI_STREAM< AMI_sm_elem > { +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 +AMI_sparse_matrix::AMI_sparse_matrix(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col) : + r(row), c(col), AMI_STREAM< AMI_sm_elem >() +{ +} + +template +AMI_sparse_matrix::~AMI_sparse_matrix(void) +{ +} + +template +TPIE_OS_OFFSET AMI_sparse_matrix::rows(void) +{ + return r; +} + +template +TPIE_OS_OFFSET AMI_sparse_matrix::cols(void) +{ + return c; +} + + +// +// A class of comparison object designed to facilitate sorting of +// elements of the spase matrix into bands. +// + +template +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 &t1, const AMI_sm_elem &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 +AMI_err AMI_sparse_bandify(AMI_sparse_matrix &sm, + AMI_sparse_matrix &bsm, + TPIE_OS_SIZE_T rows_per_band) +{ + AMI_err ae; + + sm_band_comparator cmp(rows_per_band); + + ae = AMI_sort_V1((AMI_STREAM< AMI_sm_elem > *)&sm, + (AMI_STREAM< AMI_sm_elem > *)&bsm, + (sm_band_comparator *)&cmp); + + return ae; +} + +// Get all band information for the given matrix and the current +// runtime environment. + +template +AMI_err AMI_sparse_band_info(AMI_sparse_matrix &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 +AMI_err AMI_sparse_mult_scan_banded(AMI_sparse_matrix &banded_opm, + AMI_matrix &opv, AMI_matrix &res, + TPIE_OS_OFFSET rows, TPIE_OS_OFFSET /*cols*/, + TPIE_OS_SIZE_T rows_per_band) +{ + AMI_err ae; + + AMI_sm_elem *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 +AMI_err AMI_sparse_mult(AMI_sparse_matrix &opm, AMI_matrix &opv, + AMI_matrix &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 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 diff --git a/fastlib/u/nvasil/tpie/ami_stack.h b/fastlib/u/nvasil/tpie/ami_stack.h new file mode 100644 index 0000000000..5f2cb44065 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_stack.h @@ -0,0 +1,91 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: ami_stack.h +// Author: Darren Vengroff +// 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 +// Get the AMI_STREAM definition. +#include + +template +class AMI_stack : public AMI_STREAM { + public: + using AMI_STREAM::seek; + using AMI_STREAM::truncate; + using AMI_STREAM::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 +AMI_stack::AMI_stack() : + AMI_STREAM() +{ +} + +template +AMI_stack::AMI_stack(const char* path, AMI_stream_type type): + AMI_STREAM(path, type) +{ +} + +template +AMI_stack::~AMI_stack(void) +{ +} + +template +AMI_err AMI_stack::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 +AMI_err AMI_stack::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 diff --git a/fastlib/u/nvasil/tpie/ami_stream.h b/fastlib/u/nvasil/tpie/ami_stream.h new file mode 100644 index 0000000000..9699d72fde --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_stream.h @@ -0,0 +1,99 @@ +// +// File: ami_stream.h (formerly part of ami.h and ami_imps.h) +// Author: Darren Erik Vengroff +// +// $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 + +#ifndef AMI_VIRTUAL_BASE +# define AMI_VIRTUAL_BASE 0 +#endif + +// include definition of VERSION macro +#include + +// Include the configuration header. +#include + +// Get the base class, enums, etc... +#include +#include + +// Get the device description class +#include + +// 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 + // 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 diff --git a/fastlib/u/nvasil/tpie/ami_stream_arith.h b/fastlib/u/nvasil/tpie/ami_stream_arith.h new file mode 100644 index 0000000000..af1a618380 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_stream_arith.h @@ -0,0 +1,106 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: ami_stream_arith.h +// Author: Darren Vengroff +// 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 +// Get the definition of the AMI_scan_object class. +#include + +#define SCAN_OPERATOR_DECLARATION(NAME,OP) \ + \ +template 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 \ +AMI_err AMI_scan_ ## NAME::initialize(void) \ +{ \ + return AMI_ERROR_NO_ERROR; \ +} \ + \ + \ +template \ +AMI_err AMI_scan_ ## NAME::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 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 \ +AMI_scan_scalar_ ## NAME:: \ + AMI_scan_scalar_ ## NAME(const T &s) : \ + scalar(s) \ +{ \ +} \ + \ + \ +template \ +AMI_scan_scalar_ ## NAME::~AMI_scan_scalar_ ## NAME() \ +{ \ +} \ + \ + \ +template \ +AMI_err AMI_scan_scalar_ ## NAME::initialize(void) \ +{ \ + return AMI_ERROR_NO_ERROR; \ +} \ + \ + \ +template \ +AMI_err AMI_scan_scalar_ ## NAME::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 diff --git a/fastlib/u/nvasil/tpie/ami_stream_base.h b/fastlib/u/nvasil/tpie/ami_stream_base.h new file mode 100644 index 0000000000..68ab993477 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_stream_base.h @@ -0,0 +1,93 @@ +// +// File: ami_stream_base.h (formerly ami_base.h) +// Author: Darren Erik Vengroff +// 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 +#include +#include + +// Get definitions for working with Unix and Windows +#include + +// 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 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 **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 diff --git a/fastlib/u/nvasil/tpie/ami_stream_single.cc b/fastlib/u/nvasil/tpie/ami_stream_single.cc new file mode 100644 index 0000000000..fe2a207a36 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_stream_single.cc @@ -0,0 +1,72 @@ +// +// File: ami_stream_single.cpp (formerly ami_single.cpp) +// Author: Darren Erik Vengroff +// Created: 8/24/93 +// + +#include +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 + +// 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) +{ +} diff --git a/fastlib/u/nvasil/tpie/ami_stream_single.h b/fastlib/u/nvasil/tpie/ami_stream_single.h new file mode 100644 index 0000000000..2e11856db1 --- /dev/null +++ b/fastlib/u/nvasil/tpie/ami_stream_single.h @@ -0,0 +1,512 @@ +// +// File: ami_stream_single.h (formerly ami_single.h) +// Author: Darren Erik Vengroff +// 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 + +// [tavi] for UINT_MAX +#include +#include + +// Use tempnam() instead of mktemp(). +// no - tempnam uses environment in way we dont like +#include + +// For free() +#include + +// To make assertions. +#include +#include + +// 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 + +// Get the AMI_stream_base class. +#include + +// 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 + +#include +#include + +// 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 AMI_stream_single : public AMI_stream_base, + public AMI_stream_single_base { +private: + using AMI_stream_base::status_; + + // Point to a base stream, since the particular type of BTE + // stream we are using may vary. + BTE_STREAM *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 *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 **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 +AMI_stream_single::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(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 +AMI_stream_single::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(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 +AMI_stream_single::AMI_stream_single(BTE_STREAM *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 +AMI_err AMI_stream_single::new_substream(AMI_stream_type st, + TPIE_OS_OFFSET sub_begin, + TPIE_OS_OFFSET sub_end, + AMI_stream_base **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 *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 *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 *bte_ss_b=0; + assert(sizeof(BTE_STREAM*) == sizeof(BTE_stream_base*)); + memcpy(bte_ss_b, bte_ss, sizeof(BTE_STREAM*)); + ami_ss = new AMI_stream_single(bte_ss_b); +#endif + ami_ss = new AMI_stream_single((BTE_STREAM*)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 *)ami_ss; + + return ae; +} + + +template +AMI_err AMI_stream_single::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 +AMI_err AMI_stream_single::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 +AMI_err AMI_stream_single::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 +AMI_err AMI_stream_single::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 +AMI_stream_single::~AMI_stream_single(void) +{ + if (destruct_bte) { + delete btes; + } +} + +template +A_INLINE AMI_err AMI_stream_single::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 +A_INLINE AMI_err AMI_stream_single::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 +A_INLINE AMI_err AMI_stream_single::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 +A_INLINE AMI_err AMI_stream_single::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 +int AMI_stream_single::available_streams(void) +{ + return btes->available_streams(); +} + + +template +void AMI_stream_single::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 +char *AMI_stream_single::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 diff --git a/fastlib/u/nvasil/tpie/apm_dh.h b/fastlib/u/nvasil/tpie/apm_dh.h new file mode 100644 index 0000000000..dcec438b17 --- /dev/null +++ b/fastlib/u/nvasil/tpie/apm_dh.h @@ -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 + +// Includes needed from TPIE +#include +#include +#include //For templated heaps +#include //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 mrgheap (cmp); + mrgheap.allocate (arity); + //Rewind all the input streams + for(int i=0; iseek(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 mrgheap (cmp); + mrgheap.allocate (arity); + //Rewind all the input streams + for(int i=0; iseek(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 +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 mrgheap (cmp); + mrgheap.allocate (arity); + //Rewind all the input streams + for(int i=0; iseek(0); } + + return AMI_single_merge_dh ( inStreams, arity, outStream, mrgheap); +} + +#endif //_APM_DH_H diff --git a/fastlib/u/nvasil/tpie/b_vector.h b/fastlib/u/nvasil/tpie/b_vector.h new file mode 100644 index 0000000000..06c44497ef --- /dev/null +++ b/fastlib/u/nvasil/tpie/b_vector.h @@ -0,0 +1,124 @@ +// +// File: b_vector.h +// Authors: Octavian Procopiuc +// +// 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 + +#include + +template +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& 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 +size_t b_vector::copy(size_t start, size_t length, + const b_vector& 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 +size_t b_vector::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 +void b_vector::insert(const T& t, size_t pos) { + copy(pos + 1, capacity_ - pos - 1, *this, pos); + copy(pos, 1, &t); +} + +//// *b_vector::erase* //// +template +void b_vector::erase(size_t pos) { + copy(pos, capacity_ - pos - 1, *this, pos + 1); +} + +#endif // _B_VECTOR_H diff --git a/fastlib/u/nvasil/tpie/bit.cc b/fastlib/u/nvasil/tpie/bit.cc new file mode 100644 index 0000000000..349a2c6b47 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bit.cc @@ -0,0 +1,80 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: bit.cpp +// Author: Darren Vengroff +// Created: 11/4/94 +// + +#include +VERSION(bit_cpp,"$Id: bit.cpp,v 1.5 2003/09/12 18:46:44 jan Exp $"); + +#include + +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); +} + + + + diff --git a/fastlib/u/nvasil/tpie/bit.h b/fastlib/u/nvasil/tpie/bit.h new file mode 100644 index 0000000000..5dee9c6721 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bit.h @@ -0,0 +1,42 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: bit.h +// Author: Darren Vengroff +// 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 + +#include + +// 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 diff --git a/fastlib/u/nvasil/tpie/bit_matrix.cc b/fastlib/u/nvasil/tpie/bit_matrix.cc new file mode 100644 index 0000000000..96bd0b1b63 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bit_matrix.cc @@ -0,0 +1,91 @@ +// Copyright (c) 1995 Darren Vengroff +// +// File: bit_matrix.cpp +// Author: Darren Vengroff +// Created: 1/9/95 +// + +#include +VERSION(bit_matrix_cpp,"$Id: bit_matrix.cpp,v 1.15 2005/01/14 18:42:24 tavi Exp $"); + +#include + +bit_matrix::bit_matrix(TPIE_OS_SIZE_T arows, TPIE_OS_SIZE_T acols) : + matrix(arows, acols) +{ +} + +bit_matrix::bit_matrix(matrix &mb) : + matrix(mb) +{ +} + +bit_matrix::~bit_matrix(void) +{ +} + +bit_matrix bit_matrix::operator=(const bit_matrix &rhs) { + return this->matrix::operator=((matrix &)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::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::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 sum = ((matrix &)op1) + ((matrix &)op2); + + return sum; +} + +bit_matrix operator*(const bit_matrix &op1, const bit_matrix &op2) +{ + matrix prod = ((matrix &)op1) * ((matrix &)op2); + + return prod; +} + +ostream &operator<<(ostream &s, bit_matrix &bm) +{ + return s << (matrix &)bm; +} diff --git a/fastlib/u/nvasil/tpie/bit_matrix.h b/fastlib/u/nvasil/tpie/bit_matrix.h new file mode 100644 index 0000000000..3bbb1444ac --- /dev/null +++ b/fastlib/u/nvasil/tpie/bit_matrix.h @@ -0,0 +1,49 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: bit_matrix.h +// Author: Darren Vengroff +// 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 + +#include +#include + +#include + + +// typedef matrix bit_matrix_0; + +class bit_matrix : public matrix { +public: + using matrix::rows; + using matrix::cols; + + bit_matrix(matrix &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 diff --git a/fastlib/u/nvasil/tpie/bte_coll.h b/fastlib/u/nvasil/tpie/bte_coll.h new file mode 100644 index 0000000000..26527b4bbe --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_coll.h @@ -0,0 +1,55 @@ +// +// File: bte_coll.h +// Authors: Octavian Procopiuc +// +// $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 + +// The MMAP implementation. +#include + +// The UFS implementation. +#include + +// Get definitions for working with Unix and Windows +#include + + +#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 +#define BTE_COLLECTION_UFS BTE_collection_ufs + +#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 diff --git a/fastlib/u/nvasil/tpie/bte_coll_base.h b/fastlib/u/nvasil/tpie/bte_coll_base.h new file mode 100644 index 0000000000..095b7ab583 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_coll_base.h @@ -0,0 +1,624 @@ +// Copyright (c) 2001 Octavian Procopiuc +// +// File: bte_coll_base.h +// Authors: Octavian Procopiuc +// (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 + +// Include the registration based memory manager. +#define MM_IMP_REGISTER +#include + +// For persist. +#include +// For BTE_stack_ufs +#include +// For BTE_err. +#include +// For class tpie_stats_collection. +#include + +// 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 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 *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 +tpie_stats_collection BTE_collection_base::gstats_; + +template +void BTE_collection_base::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((char *) stack_name, + read_only_? BTE_READ_STREAM: BTE_WRITE_STREAM); + +} + +template +void BTE_collection_base::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 +BTE_collection_base::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 +void BTE_collection_base::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 +bool BTE_collection_base::direct_io = false; +#endif + + +template +BTE_err BTE_collection_base::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 +BTE_err BTE_collection_base::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 +BTE_collection_base::~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 diff --git a/fastlib/u/nvasil/tpie/bte_coll_mmap.h b/fastlib/u/nvasil/tpie/bte_coll_mmap.h new file mode 100644 index 0000000000..f2059f4a25 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_coll_mmap.h @@ -0,0 +1,221 @@ +// +// File: bte_coll_mmap.h (formerly bte_coll_mmb.h) +// Author: Octavian Procopiuc +// +// $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 +// Get the base class. +#include + +// 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 BTE_collection_mmap: public BTE_collection_base { + protected: + using BTE_collection_base::header_; + using BTE_collection_base::freeblock_stack_; + using BTE_collection_base::bcc_fd_; + using BTE_collection_base::per_; + using BTE_collection_base::os_block_size_; + using BTE_collection_base::base_file_name_; + using BTE_collection_base::status_; + using BTE_collection_base::read_only_; + using BTE_collection_base::in_memory_blocks_; + using BTE_collection_base::file_pointer; + using BTE_collection_base::stats_; + using BTE_collection_base::gstats_; + using BTE_collection_base::register_memory_allocation; + using BTE_collection_base::register_memory_deallocation; + using BTE_collection_base::bid_to_file_offset; + using BTE_collection_base::create_stack; + using BTE_collection_base::new_block_getid; + using BTE_collection_base::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(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 +BTE_err BTE_collection_mmap::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 +BTE_err BTE_collection_mmap::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 +BTE_err BTE_collection_mmap::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 diff --git a/fastlib/u/nvasil/tpie/bte_coll_ufs.h b/fastlib/u/nvasil/tpie/bte_coll_ufs.h new file mode 100644 index 0000000000..ae1a7b94bb --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_coll_ufs.h @@ -0,0 +1,299 @@ +// +// File: bte_coll_ufs.h +// Author: Octavian Procopiuc +// +// $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 +// Get the base class. +#include + +// For header's type field (85 == 'U'). +#define BTE_COLLECTION_UFS_ID 85 + +template +class BTE_collection_ufs: public BTE_collection_base { + protected: + using BTE_collection_base::header_; + using BTE_collection_base::freeblock_stack_; + using BTE_collection_base::bcc_fd_; + using BTE_collection_base::per_; + using BTE_collection_base::os_block_size_; + using BTE_collection_base::base_file_name_; + using BTE_collection_base::status_; + using BTE_collection_base::read_only_; + using BTE_collection_base::in_memory_blocks_; + using BTE_collection_base::file_pointer; + using BTE_collection_base::stats_; + using BTE_collection_base::gstats_; + using BTE_collection_base::register_memory_allocation; + using BTE_collection_base::register_memory_deallocation; + using BTE_collection_base::bid_to_file_offset; + using BTE_collection_base::create_stack; + using BTE_collection_base::new_block_getid; + using BTE_collection_base::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(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 +BTE_err BTE_collection_ufs::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 +BTE_err BTE_collection_ufs::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 +BTE_err BTE_collection_ufs::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 +BTE_err BTE_collection_ufs::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 +BTE_err BTE_collection_ufs::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 diff --git a/fastlib/u/nvasil/tpie/bte_err.h b/fastlib/u/nvasil/tpie/bte_err.h new file mode 100644 index 0000000000..393b5d8c5c --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_err.h @@ -0,0 +1,37 @@ +// +// File: bte_err.h +// Author: Octavian Procopiuc +// (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 + +// +// 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 diff --git a/fastlib/u/nvasil/tpie/bte_stack_ufs.h b/fastlib/u/nvasil/tpie/bte_stack_ufs.h new file mode 100644 index 0000000000..5cb2c1291f --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_stack_ufs.h @@ -0,0 +1,91 @@ +// +// File: bte_stack_ufs.h +// Author: Octavian Procopiuc +// 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 + +#include + +template +class BTE_stack_ufs : public BTE_stream_ufs { +public: + using BTE_stream_ufs::stream_len; + using BTE_stream_ufs::seek; + using BTE_stream_ufs::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 +BTE_stack_ufs::BTE_stack_ufs(char *path, + BTE_stream_type type) : + BTE_stream_ufs(path, type, 1) +{ +} + +template +BTE_stack_ufs::~BTE_stack_ufs(void) +{ +} + +template +BTE_err BTE_stack_ufs::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 +BTE_err BTE_stack_ufs::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 diff --git a/fastlib/u/nvasil/tpie/bte_stream.h b/fastlib/u/nvasil/tpie/bte_stream.h new file mode 100644 index 0000000000..0217b60d35 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_stream.h @@ -0,0 +1,117 @@ +// +// File: bte_stream.h (formerly bte.h) +// Author: Darren Erik Vengroff +// 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 + +#ifndef BTE_VIRTUAL_BASE +# define BTE_VIRTUAL_BASE 0 +#endif + +// Get the base class, enums, etc... +#include + +#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 + // 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 + // 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 + // 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 diff --git a/fastlib/u/nvasil/tpie/bte_stream_base.cc b/fastlib/u/nvasil/tpie/bte_stream_base.cc new file mode 100644 index 0000000000..4975cb4811 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_stream_base.cc @@ -0,0 +1,21 @@ +// +// File: bte_stream_base.cpp +// Author: Octavian Procopiuc +// (using some code by Darren Erik Vengroff) +// Created: 01/08/02 +// + +#include "lib_config.h" +#include +VERSION(bte_stream_base_cpp,"$Id: bte_stream_base.cpp,v 1.3 2003/04/23 07:32:15 tavi Exp $"); + +#include + +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(); + diff --git a/fastlib/u/nvasil/tpie/bte_stream_base.h b/fastlib/u/nvasil/tpie/bte_stream_base.h new file mode 100644 index 0000000000..13f1f0b076 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_stream_base.h @@ -0,0 +1,265 @@ +// +// File: bte_stream_base.h (formerly bte_base_stream.h) +// Author: Darren Erik Vengroff +// 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 + +#include +// Get the BTE error codes. +#include +// Get statistics definitions. +#include + +// Include the registration based memory manager. +#define MM_IMP_REGISTER +#include + +// 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 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 **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 +int BTE_stream_base::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 +void BTE_stream_base::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 +BTE_err BTE_stream_base::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 +BTE_err BTE_stream_base::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 +TPIE_OS_SIZE_T BTE_stream_base::os_block_size () const { + return TPIE_OS_BLOCKSIZE(); +} + +#endif // _BTE_STREAM_BASE_H diff --git a/fastlib/u/nvasil/tpie/bte_stream_cache.h b/fastlib/u/nvasil/tpie/bte_stream_cache.h new file mode 100644 index 0000000000..593f2a0613 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_stream_cache.h @@ -0,0 +1,233 @@ +// +// File: bte_stream_cache.h (formerly bte_cache.h) +// Author: Darren Erik Vengroff +// 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 + +// Include the registration based memory manager. +#define MM_IMP_REGISTER +#include + +#include + +// This code makes assertions and logs errors. +#include +#include + + +#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 BTE_stream_cache : public BTE_stream_base { +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 **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 +BTE_stream_cache::BTE_stream_cache(void) +{ +}; + +template +BTE_stream_cache::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 +BTE_err BTE_stream_cache::new_substream(BTE_stream_type st, TPIE_OS_OFFSET sub_begin, + TPIE_OS_OFFSET sub_end, + BTE_stream_base **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 *)ss; + return BTE_ERROR_NO_ERROR; + } +}; + + +template +BTE_err BTE_stream_cache::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 +TPIE_OS_OFFSET BTE_stream_cache::stream_len(void) +{ + return data_max - data; +}; + + + +template +BTE_err BTE_stream_cache::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 +BTE_stream_cache::~BTE_stream_cache(void) +{ + if (!substream_level) { + delete data; + } +}; + +template +BTE_err BTE_stream_cache::read_item(T **elt) +{ + if (current >= data_max) { + return BTE_ERROR_END_OF_STREAM; + } else { + *elt = current++; + return BTE_ERROR_NO_ERROR; + } +}; + +template +BTE_err BTE_stream_cache::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 +TPIE_OS_OFFSET BTE_stream_cache::chunk_size(void) +{ + return BTE_STREAM_CACHE_LINE_SIZE / sizeof(T); +} + + +#endif // _BTE_STREAM_CACHE_H diff --git a/fastlib/u/nvasil/tpie/bte_stream_mmap.h b/fastlib/u/nvasil/tpie/bte_stream_mmap.h new file mode 100644 index 0000000000..6fcb2e5792 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_stream_mmap.h @@ -0,0 +1,1544 @@ +// +// File: bte_stream_mmap.h (formerly bte_mmb.h) +// Author: Darren Erik Vengroff +// Created: 5/13/94 +// +// $Id: bte_stream_mmap.h,v 1.16 2005/07/07 20:36:12 adanner Exp $ +// +// Memory mapped streams. This particular implementation explicitly manages +// blocks, and only ever maps in one block at a time. +// +// TODO: Get rid of or fix the LIBAIO stuff. As it is now it has no +// chance of working, since it uses the static +// BTE_STREAM_MMAP_BLOCK_FACTOR, which is no longer the true +// factor. The true block factor is determined dynamically, from the +// header. +// + +#ifndef _BTE_STREAM_MMAP_H +#define _BTE_STREAM_MMAP_H + +#ifdef VERBOSE +# include +#endif + +// Get definitions for working with Unix and Windows +#include + +// For header's type field (77 == 'M'). +#define BTE_STREAM_MMAP 77 + +#include + +#if USE_LIBAIO +# if !HAVE_LIBAIO +# error USE_LIBAIO requested, but aio library not in configuration. +# endif +# include +#endif + +#ifdef BTE_STREAM_MMAP_READ_AHEAD +# define BTE_STREAM_MMAP_MM_BUFFERS 2 +#else +# define BTE_STREAM_MMAP_MM_BUFFERS 1 +#endif + +// Get the BTE_stream_base class and other definitions. +#include + +// This code makes assertions and logs errors. +#include +#include + +#ifndef BTE_STREAM_MMAP_BLOCK_FACTOR +# define BTE_STREAM_MMAP_BLOCK_FACTOR 8 +#endif + +// Figure out the block offset for an offset (pos) in file. +// os_block_size_ is assumed to be the header size. +#define BLOCK_OFFSET(pos) (((pos - os_block_size_) / header->block_size) * header->block_size + os_block_size_) + + +// +// BTE_stream_mmap +// +// This is a class template for the mmap() based implementation of a +// BTE stream of objects of type T. This version maps in only one +// block of the file at a time. +// +template < class T > class BTE_stream_mmap: public BTE_stream_base < T > { +private: + unsigned int mmap_status; + + TPIE_OS_FILE_DESCRIPTOR fd; // descriptor of the mapped file. + + + size_t os_block_size_; + + // Offset of the current item in the file. This is the logical + // offset of the item within the file, that is, the place we would + // have to lseek() to in order to read() or write() the item if we + // were using ordinary (i.e. non-mmap()) file access methods. + TPIE_OS_OFFSET f_offset; + + // Offset just past the end of the last item in the stream. If this + // is a substream, we can't write here or anywhere beyond. + TPIE_OS_OFFSET f_eos; + + // Length of the file in the file system. this is the first offset + // that would be not part of the file. Different from f_eos since + // we can grow the file independently of the actual writes. + TPIE_OS_OFFSET f_filelen; + + // Beginning of the file. Can't write before here. + TPIE_OS_OFFSET f_bos; + + // A pointer to the mapped in header block for the stream. + BTE_stream_header *header; + + // Pointer to the current item (mapped in). + T *current; + // Pointer to beginning of the currently mapped block. + T *curr_block; + // Non-zero if current points to a valid, mapped block. + int block_valid; + // true if the curr_block is mapped. + int block_mapped; + + // for use in double buffering + T *next_block; // ptr to next block + TPIE_OS_OFFSET f_next_block; // position of next block + int have_next_block; // is next block mapped + int w_only; // stream is write-only + + // A place to cache OS error values. It is normally set after each + // call to the OS. + int os_errno; + + char path[BTE_STREAM_PATH_NAME_LEN]; + +#if USE_LIBAIO + // A buffer to read the first word of each OS block in the next logical + // block for read ahead. + int read_ahead_buffer[BTE_STREAM_MMAP_BLOCK_FACTOR]; + + // Results of asyncronous I/O. + aio_result_t aio_results[BTE_STREAM_MMAP_BLOCK_FACTOR]; +#endif + +#ifdef BTE_STREAM_MMAP_READ_AHEAD + // Read ahead into the next logical block. + void read_ahead (); +#endif + + void initialize (); + + BTE_stream_header *map_header (); + void unmap_header (); + + inline BTE_err validate_current (); + BTE_err map_current (); + inline BTE_err invalidate_current (); + BTE_err unmap_current (); + + inline BTE_err advance_current (); + + inline TPIE_OS_OFFSET item_off_to_file_off (TPIE_OS_OFFSET item_off) const; + inline TPIE_OS_OFFSET file_off_to_item_off (TPIE_OS_OFFSET item_off) const; + +#ifdef COLLECT_STATS + long stats_hits; + long stats_misses; + long stats_compulsory; + long stats_eos; +#endif + + protected: + using BTE_stream_base::remaining_streams; + using BTE_stream_base::gstats_; + using BTE_stream_base::status_; + using BTE_stream_base::stats_; + using BTE_stream_base::substream_level; + using BTE_stream_base::per; + using BTE_stream_base::r_only; + public: + using BTE_stream_base::os_block_size; + using BTE_stream_base::check_header; + using BTE_stream_base::init_header; + using BTE_stream_base::register_memory_allocation; + using BTE_stream_base::register_memory_deallocation; + + public: + // Constructor. + // [tavi 01/09/02] Careful with the lbf (logical block factor) + // parameter. I introduced it in order to avoid errors when reading + // a stream having a different block factor from the default, but + // this may cause errors in applications. For example, + // AMI_partition_and merge computes memory requirements of temporary + // streams based on the memory usage of the INPUT stream. However, + // the input stream may have different block size from the temporary + // streams created later. Until these issues are addressed, the + // usage of lbf is discouraged. + BTE_stream_mmap (const char *dev_path, BTE_stream_type st, + size_t lbf = BTE_STREAM_MMAP_BLOCK_FACTOR); + + // A substream constructor. + BTE_stream_mmap (BTE_stream_mmap * super_stream, + BTE_stream_type st, TPIE_OS_OFFSET sub_begin, TPIE_OS_OFFSET sub_end); + + // 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 () 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); + + // Destructor + ~BTE_stream_mmap (); + + B_INLINE BTE_err read_item (T ** elt); + B_INLINE BTE_err write_item (const T & elt); + + TPIE_OS_OFFSET chunk_size () const; + + void print (char *pref = ""); + inline BTE_err grow_file (TPIE_OS_OFFSET block_offset); + + static void log_fatal (char *a, char *b, char *c, char *d, char *e) { + TP_LOG_FATAL (a); + TP_LOG_FATAL (b); + TP_LOG_FATAL (c); + TP_LOG_FATAL (d); + TP_LOG_FATAL (e); + TP_LOG_FLUSH_LOG; + }; +}; + +/* ********************************************************************** */ + +/* definitions start here */ + +static int call_munmap (void *addr, size_t len) +{ + + // int rv; + // + //#ifdef MACH_ALPHA + // rv = TPIE_OS_MUNMAP (addr, len); + //#else + // rv = TPIE_OS_MUNMAP ((caddr_t) addr, len); + //#endif + // return rv; + + return TPIE_OS_MUNMAP (addr, len); +} + +static void *call_mmap (void *addr, size_t len, int r_only, int w_only, + TPIE_OS_FILE_DESCRIPTOR fd, TPIE_OS_OFFSET off, int fixed) +{ + void *ptr; + int flags = 0; + + assert (!fixed || addr); + +#ifdef MACH_ALPHA + // for enhanced mmap calls +#define MAP_OVERWRITE 0x1000 // block will be overwritten + flags = (MAP_FILE | + (fixed ? TPIE_OS_FLAG_MAP_FIXED :MAP_VARIABLE) | + (w_only ? MAP_OVERWRITE : 0)); +#else + flags = (fixed ? TPIE_OS_FLAG_MAP_FIXED : 0); +#endif + flags |= TPIE_OS_FLAG_MAP_SHARED; + + //#ifdef MACH_ALPHA + // ptr = TPIE_OS_MMAP (addr, len, + // (r_only ? PROT_READ : PROT_READ | PROT_WRITE), + // flags, fd, off); + //#else + // ptr = TPIE_OS_MMAP ((caddr_t) addr, len, + // (r_only ? PROT_READ : PROT_READ | PROT_WRITE), + // flags, fd, off); + //#endif + + ptr = TPIE_OS_MMAP (addr, len, + (r_only ? TPIE_OS_FLAG_PROT_READ : TPIE_OS_FLAG_PROT_READ | TPIE_OS_FLAG_PROT_WRITE), + flags, fd, off); + assert (ptr); + return ptr; +} + + +template < class T > void BTE_stream_mmap < T >::initialize () +{ +#ifdef COLLECT_STATS + stats_misses = stats_hits = stats_compulsory = stats_eos = 0; +#endif + have_next_block = 0; + block_valid = 0; + block_mapped = 0; + f_offset = f_bos = os_block_size_; + next_block = curr_block = current = NULL; + ///f_stats = 1; +} + +// +// This constructor creates a stream whose contents are taken from the +// file whose path is given. +// +template < class T > +BTE_stream_mmap < T >::BTE_stream_mmap (const char *dev_path, BTE_stream_type st, size_t lbf) { + status_ = BTE_STREAM_STATUS_NO_STATUS; + + if (remaining_streams <= 0) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("BTE internal error: cannot open more streams."); + 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 \"" << dev_path << "\" too long."); + return; + } + + strncpy (path, dev_path, BTE_STREAM_PATH_NAME_LEN); + r_only = (st == BTE_READ_STREAM); + w_only = (st == BTE_WRITEONLY_STREAM); + + os_block_size_ = os_block_size(); + + // This is a top level stream + substream_level = 0; + // Reduce the number of streams available. + remaining_streams--; + + switch (st) { + case BTE_READ_STREAM: + // Open the file for reading. + if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(fd = TPIE_OS_OPEN_ORDONLY(path, TPIE_OS_FLAG_USE_MAPPING_TRUE))) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + log_fatal ("open() failed to open \"", path, "\": ", + strerror (os_errno), "\n"); + // [tavi 01/07/02] Commented this out. No need to panic. + //assert (0); + return; + } + // Get ready to read the first item out of the file. + initialize (); + header = map_header (); + if (check_header (header) < 0) { + status_ = BTE_STREAM_STATUS_INVALID; + // [tavi 01/07/02] Commented this out. No need to panic. + //assert (0); + return; + } + if (header->type != BTE_STREAM_MMAP) { + TP_LOG_WARNING_ID("Using MMAP stream implem. on another type of stream."); + TP_LOG_WARNING_ID("Stream implementations may not be compatible."); + } + if ((header->block_size % os_block_size_ != 0) || + (header->block_size == 0)) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("header: incorrect logical block size;"); + TP_LOG_FATAL_ID ("expected multiple of OS block size."); + return; + } + if (header->block_size != BTE_STREAM_MMAP_BLOCK_FACTOR * os_block_size_) { + TP_LOG_WARNING_ID("Stream has different block factor than the default."); + TP_LOG_WARNING_ID("This may cause problems in some existing applications."); + } + break; + + case BTE_WRITE_STREAM: + case BTE_WRITEONLY_STREAM: + case BTE_APPEND_STREAM: + // Open the file for writing. First we will try to open + // is 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(fd = TPIE_OS_OPEN_OEXCL(path, TPIE_OS_FLAG_USE_MAPPING_TRUE))) { + + // Try again, hoping the file already exists. + if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(fd = TPIE_OS_OPEN_ORDWR(path, TPIE_OS_FLAG_USE_MAPPING_TRUE))) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + log_fatal ("open() failed to open \"", path, "\": ", + strerror (os_errno), "\n"); + return; + } + initialize (); + // The file already exists, so read the header. + header = map_header (); + if (check_header (header) < 0) { + status_ = BTE_STREAM_STATUS_INVALID; + // [tavi 01/07/02] Commented this out. No need to panic. + //assert (0); + return; + } + if (header->type != BTE_STREAM_MMAP) { + TP_LOG_WARNING_ID("Using MMAP stream implem. on another type of stream."); + TP_LOG_WARNING_ID("Stream implementations may not be compatible."); + } + if ((header->block_size % os_block_size_ != 0) || + (header->block_size == 0)) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("header: incorrect logical block size;"); + TP_LOG_FATAL_ID ("expected multiple of OS block size."); + return; + } + if (header->block_size != BTE_STREAM_MMAP_BLOCK_FACTOR * os_block_size_) { + TP_LOG_WARNING_ID("Stream has different block factor than the default."); + TP_LOG_WARNING_ID("This may cause problems in some existing applications."); + } + } else { // The file was just created. + + f_eos = os_block_size_; + // Rajiv + // [tavi 01/07/02] Commented this out. Aren't we sure the file is OK? + //assert (lseek (fd, 0, SEEK_END) == 0); + +#ifdef VERBOSE + if (verbose) + cout << "CONS created file: " << path << "\n"; +#endif + + // what does this do??? Rajiv + // Create and map in the header. + if (TPIE_OS_LSEEK(fd, os_block_size_ - 1, TPIE_OS_FLAG_SEEK_SET) != os_block_size_ - 1) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + log_fatal ("lseek() failed to move past header of \"", + path, "\": ", strerror (os_errno), "\n"); + // [tavi 01/07/02] Commented this out. No need to panic. + //assert (0 == 1); + return; + } + initialize (); + header = map_header (); + if (header == NULL) { + status_ = BTE_STREAM_STATUS_INVALID; + return; + } + init_header (header); + + if (lbf == 0) { + lbf = 1; + TP_LOG_WARNING_ID("Block factor 0 requested. Using 1 instead."); + } + // Set the logical block size. + header->block_size = lbf * os_block_size_; + // Set the type. + header->type = BTE_STREAM_MMAP; + gstats_.record(STREAM_CREATE); + stats_.record(STREAM_CREATE); + } + break; + } + + // We can't handle streams of large objects. + if (sizeof (T) > header->block_size) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("Object is too big (object size/block size):"); + TP_LOG_FATAL_ID (sizeof(T)); + TP_LOG_FATAL_ID (static_cast(header->block_size)); + return; + } + f_filelen = TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_END); + assert (f_filelen >= 0); + f_eos = item_off_to_file_off (header->item_logical_eof); + if (st == BTE_APPEND_STREAM) { + f_offset = f_eos; + } else { + f_offset = os_block_size_; + } +#ifdef VERBOSE + // Rajiv + if (verbose) + cout << "CONS logical eof=" << header->item_logical_eof << "\n"; +#endif + + // By default, all streams are deleted at destruction time. + // [tavi 01/07/02] No. Streams initialized with given names are persistent. + per = PERSIST_PERSISTENT; + + // Register memory usage before returning. + // Since blocks and header are allocated by mmap and not "new", + // register memory manually. No mem overhead with mmap + register_memory_allocation (sizeof (BTE_stream_header)); + register_memory_allocation (BTE_STREAM_MMAP_MM_BUFFERS * header->block_size); + gstats_.record(STREAM_OPEN); + stats_.record(STREAM_OPEN); + +#ifdef VERBOSE + // Rajiv + if (verbose) { + switch (st) { + case BTE_READ_STREAM: + cout << "CONS read stream\n"; + break; + case BTE_WRITE_STREAM: + cout << "CONS read/write stream\n"; + break; + default: + cout << "CONS someother stream\n"; + break; + } + print ("CONS "); + } +#endif +} + + +// A substream constructor. +// sub_begin is the item offset of the first item in the stream. +// sub_end is the item offset that of the last item in the stream. +// Thus, f_eos in the new substream will be set to point one item beyond +// this. +// +// For example, if a stream contains [A,B,C,D,...] then substream(1,3) +// will contain [B,C,D]. +template < class T > +BTE_stream_mmap < T >::BTE_stream_mmap (BTE_stream_mmap * super_stream, BTE_stream_type st, TPIE_OS_OFFSET sub_begin, TPIE_OS_OFFSET sub_end) { + + status_ = BTE_STREAM_STATUS_NO_STATUS; + + if (remaining_streams <= 0) { + TP_LOG_FATAL_ID ("BTE error: cannot open more streams."); + status_ = BTE_STREAM_STATUS_INVALID; + return; + } + + if (super_stream->status_ == BTE_STREAM_STATUS_INVALID) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("BTE error: super stream is invalid."); + return; + } + + if (super_stream->r_only && (st != BTE_READ_STREAM)) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID + ("BTE error: super stream is read only and substream is not."); + return; + } + // Rajiv + initialize (); + + // Reduce the number of streams avaialble. + remaining_streams--; + // Copy the relevant fields from the super_stream. + fd = super_stream->fd; + os_block_size_ = super_stream->os_block_size_; + header = super_stream->header; + f_filelen = super_stream->f_filelen; + substream_level = super_stream->substream_level + 1; + + per = PERSIST_PERSISTENT; + + // The arguments sub_start and sub_end are logical item positions + // within the stream. We need to convert them to offsets within + // the stream where items are found. + + TPIE_OS_OFFSET super_item_begin = file_off_to_item_off (super_stream->f_bos); + + f_bos = item_off_to_file_off (super_item_begin + sub_begin); + f_eos = item_off_to_file_off (super_item_begin + sub_end + 1); + + if (f_eos > super_stream->f_eos) { + status_ = BTE_STREAM_STATUS_INVALID; + return; + } + + f_offset = f_bos; + + curr_block = NULL; + block_valid = 0; + + r_only = super_stream->r_only; + w_only = super_stream->w_only; + + strncpy (path, super_stream->path, BTE_STREAM_PATH_NAME_LEN); + gstats_.record(STREAM_OPEN); + gstats_.record(SUBSTREAM_CREATE); + stats_.record(STREAM_OPEN); + stats_.record(SUBSTREAM_CREATE); + + // substreams are considered to have no memory overhead! +} + +// A psuedo-constructor for substreams. This serves as a wrapper for +// the constructor above in order to get around the fact that one +// cannot have virtual constructors. +template < class T > +BTE_err BTE_stream_mmap < 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."); + + BTE_stream_mmap < T > *sub = + new BTE_stream_mmap < T > (this, st, sub_begin, sub_end); + + *sub_stream = (BTE_stream_base < T > *)sub; + + return BTE_ERROR_NO_ERROR; +} + +template < class T > BTE_stream_mmap < T >::~BTE_stream_mmap (void) { + + // If the stream is already invalid for some reason, then don't + // worry about anything. + if (status_ == BTE_STREAM_STATUS_INVALID) { + TP_LOG_WARNING_ID ("BTE internal error: invalid stream in destructor."); + return; + } + // Increase the number of streams avaialble. + if (remaining_streams >= 0) { + remaining_streams++; + } + // If this is writable and not a substream, then put the logical + // eos back into the header before unmapping it. + if (!r_only && !substream_level) { + header->item_logical_eof = file_off_to_item_off (f_eos); + } +#ifdef VERBOSE + // Rajiv + if (verbose) { + cout << "DELE logical eof=" << header->item_logical_eof << "\n"; + } +#endif + assert (substream_level || + header->item_logical_eof == file_off_to_item_off (f_eos)); + + // Unmap the current block if necessary. + if (block_mapped) { + unmap_current(); + } + // If this is not a substream then close the file. + if (!substream_level) { + // [Rajiv] make sure the length of the file is correct + // [tavi 06/23/02] and file is not read-only! + if ((f_filelen > f_eos) && (!r_only) && + (TPIE_OS_FTRUNCATE (fd, BLOCK_OFFSET(f_eos) + header->block_size) < 0)) { + os_errno = errno; + TP_LOG_FATAL_ID("Failed to ftruncate() to the new end of " << path); + TP_LOG_FATAL_ID("f_filelen:" << f_filelen << ", f_eos:" << f_eos); + TP_LOG_FATAL_ID("argument to ftruncate:" << BLOCK_OFFSET(f_eos) + header->block_size); + TP_LOG_FATAL_ID(strerror (os_errno)); + } + + // Unmap the header. + // [tavi 06/23/02] Added test for r_only. + if (!r_only) + unmap_header(); + // Close the file. + if (TPIE_OS_CLOSE (fd)) { + os_errno = errno; + TP_LOG_WARNING_ID("Failed to close() " << path); + TP_LOG_WARNING_ID(strerror (os_errno)); + } + // If it should not persist, unlink the file. + if (per == PERSIST_DELETE) { + if (r_only) { + TP_LOG_WARNING_ID("PERSIST_DELETE for read-only stream in " << path); + } + else { + if (TPIE_OS_UNLINK (path)) { + os_errno = errno; + TP_LOG_WARNING_ID ("unlink() failed during destruction of " << path); + TP_LOG_WARNING_ID (strerror (os_errno)); + } else { + gstats_.record(STREAM_DELETE); + stats_.record(STREAM_DELETE); + } + } + } + + // Register memory deallocation before returning. + register_memory_deallocation (sizeof (BTE_stream_header)); // for the header. + register_memory_deallocation (BTE_STREAM_MMAP_MM_BUFFERS * + header->block_size); + + // Free the in-memory header (previously allocated with malloc). + free (header); + + } else { + gstats_.record(SUBSTREAM_DELETE); + stats_.record(SUBSTREAM_DELETE); + } + + gstats_.record(STREAM_CLOSE); + stats_.record(STREAM_CLOSE); + +#ifdef VERBOSE + if (verbose) { + if (per == PERSIST_DELETE) + cout << "DELE unlinked file\n"; + if (substream_level) { + cout << "DELE substream destructor\n"; + } + print ("DELE "); + } +#endif +} + +// pref = prefix +template < class T > void BTE_stream_mmap < T >::print (char *pref) { + +#ifdef COLLECT_STATS +#ifdef BTE_STREAM_MMAP_READ_AHEAD + fprintf (stdout, "%sPFSTATS %d %d %d %d (fd=%d)\n", + pref, stats_hits, stats_misses, + stats_compulsory - stats_misses, stats_eos, fd); + cout << pref << stats << " RWMUSCD\n"; +#endif +#endif + fprintf (stdout, "%sfile=%s", pref, path); + fprintf (stdout, ", f_eos=%d, f_filelen=%d", f_eos, f_filelen); + fprintf (stdout, ", length=%d\n", + file_off_to_item_off (f_eos) - file_off_to_item_off (f_bos)); + fprintf (stdout, "\n"); +} + +// f_eos points just past the last item ever written, so if f_current +// is there we are at the end of the stream and cannot read. + +template < class T > +B_INLINE BTE_err BTE_stream_mmap < T >::read_item (T ** elt) { + + BTE_err bte_err; + + if (w_only) { +#ifdef VERBOSE + if (verbose) + cerr << "ERROR read on a write-only stream\n"; +#endif + return BTE_ERROR_WRITE_ONLY; + } + // Make sure we are not currently at the EOS. + if (f_offset + sizeof (T) > f_eos) { + //tp_assert(0, "Can't read past eos."); + //TP_LOG_WARNING("Reading past eos.\n"); + return BTE_ERROR_END_OF_STREAM; + } + // Validate the current block. + if ((bte_err = validate_current ()) != BTE_ERROR_NO_ERROR) { + return bte_err; + } + // Check and make sure that the current pointer points into the current + // block. + tp_assert (((char *) current - (char *) curr_block <= + header->block_size - sizeof (T)), + "current is past the end of the current block"); + tp_assert (((char *) current - (char *) curr_block >= 0), + "current is before the begining of the current block"); + + gstats_.record(ITEM_READ); + stats_.record(ITEM_READ); + + *elt = current; // Read + advance_current (); // move ptr to next elt + + // If we are in a substream, there should be no way for f_current to + // pass f_eos. + tp_assert (!substream_level || (f_offset <= f_eos), + "Got past eos in a substream."); + + return BTE_ERROR_NO_ERROR; +} + +// f_eos points just past the last item ever written, so if f_current +// is there we are at the end of the stream and can only write if this +// is not a substream. +template < class T > +B_INLINE BTE_err BTE_stream_mmap < T >::write_item (const T & elt) { + + BTE_err bte_err; + + /// if (f_stats) + /// stats.record_write (); + + // This better be a writable stream. + if (r_only) { + TP_LOG_WARNING_ID ("write on a read-only stream\n"); + return BTE_ERROR_READ_ONLY; + } + // Make sure we are not currently at the EOS of a substream. + if (substream_level && (f_eos <= f_offset)) { + tp_assert (f_eos == f_offset, "Went too far in a substream."); + return BTE_ERROR_END_OF_STREAM; + } + // Validate the current block. + bte_err = validate_current (); + if (bte_err != BTE_ERROR_NO_ERROR) { + return bte_err; + } + // Check and make sure that the current pointer points into the current + // block. + tp_assert (((char *) current - (char *) curr_block <= + header->block_size - sizeof (T)), + "current is past the end of the current block"); + tp_assert (((char *) current - (char *) curr_block >= 0), + "current is before the begining of the current block"); + assert (current); + + gstats_.record(ITEM_WRITE); + stats_.record(ITEM_WRITE); + + *current = elt; // write + advance_current (); // Advance the current pointer. + + // If we are in a substream, there should be no way for f_current + // to pass f_eos. + tp_assert (!substream_level || (f_offset <= f_eos), + "Got past eos in a substream."); + + // If we moved past eos, then update eos unless we are in a + // substream, in which case EOS will be returned on the next call. + if ((f_offset > f_eos) && !substream_level) { + // I dont like this assert Rajiv + // tp_assert(f_offset == f_eos + sizeof(T), "Advanced too far somehow."); + tp_assert (f_offset <= f_filelen, "Advanced too far somehow."); + f_eos = f_offset; + // this is the only place f_eos is changed excluding + // constructors and truncate Rajiv + } + + return BTE_ERROR_NO_ERROR; +} + +// Query memory usage + +// Note that in a substream we do not charge for the memory used by +// the header, since it is accounted for in the 0 level superstream. +template < class T > +BTE_err BTE_stream_mmap < T >::main_memory_usage (size_t * usage, MM_stream_usage usage_type) { + switch (usage_type) { + case MM_STREAM_USAGE_OVERHEAD: + //Fixed costs. Only 2*mem overhead, because only class and base + //are allocated dynamicall via "new". Header is read via mmap + *usage = sizeof(*this) + sizeof(BTE_stream_header) + + 2*MM_manager.space_overhead(); + break; + case MM_STREAM_USAGE_BUFFER: + //no mem manager overhead when allocated via mmap + *usage = BTE_STREAM_MMAP_MM_BUFFERS * header->block_size; + break; + case MM_STREAM_USAGE_CURRENT: + *usage = (sizeof(*this) + sizeof(BTE_stream_header) + + 2*MM_manager.space_overhead() + + ((curr_block == NULL) ? 0 : + BTE_STREAM_MMAP_MM_BUFFERS * header->block_size)); + break; + case MM_STREAM_USAGE_MAXIMUM: + case MM_STREAM_USAGE_SUBSTREAM: + *usage = (sizeof(*this) + sizeof(BTE_stream_header) + + 2*MM_manager.space_overhead() + + BTE_STREAM_MMAP_MM_BUFFERS * header->block_size); + break; + } + + return BTE_ERROR_NO_ERROR; +}; + +// Return the number of items in the stream. +template < class T > +TPIE_OS_OFFSET BTE_stream_mmap < T >::stream_len () const { + return file_off_to_item_off (f_eos) - file_off_to_item_off (f_bos); +}; + +// Return the path name in newly allocated space. +template < class T > +BTE_err BTE_stream_mmap < T >::name (char **stream_name) +{ + 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_mmap < T >::seek (TPIE_OS_OFFSET offset) { + + BTE_err be; + TPIE_OS_OFFSET new_offset; + + // Looks like we can only seek within the file Rajiv + if ((offset < 0) || + (offset > + file_off_to_item_off (f_eos) - file_off_to_item_off (f_bos))) { + return BTE_ERROR_OFFSET_OUT_OF_RANGE; + } + // Compute the new offset + new_offset = + item_off_to_file_off (file_off_to_item_off (f_bos) + offset); + if (r_only) { + tp_assert (new_offset <= f_eos, "Advanced too far somehow."); + } + // // If it is not in the same block as the current position then + // // invalidate the current block. + //if (((new_offset - os_block_size_) / header->block_size) != + // ((f_offset - os_block_size_) / header->block_size)) { + + // The above was the old code which was wrong: we also need to check that + // we have the correct block mapped in (f_offset does not always point into + // the current block!) + + if (((char *) current - (char *) curr_block >= header->block_size) || + (((new_offset - os_block_size_) / header->block_size) != + ((f_offset - os_block_size_) / header->block_size))) { +/* + if (block_valid) { + if ((be = invalidate_current ()) != BTE_ERROR_NO_ERROR) +*/ + if (block_valid && ((be = unmap_current()) != BTE_ERROR_NO_ERROR)) { + return be; + } + } else { + if (block_valid) { + + // We have to adjust current. + + register TPIE_OS_OFFSET internal_block_offset; + + internal_block_offset = file_off_to_item_off (new_offset) % + (header->block_size / sizeof (T)); + + current = curr_block + internal_block_offset; + } + } + + f_offset = new_offset; + + gstats_.record(ITEM_SEEK); + stats_.record(ITEM_SEEK); + return BTE_ERROR_NO_ERROR; +} + +template < class T > +TPIE_OS_OFFSET BTE_stream_mmap < T >::tell() const { + return file_off_to_item_off(f_offset); +} + +// Truncate the stream. +template < class T > +BTE_err BTE_stream_mmap < T >::truncate (TPIE_OS_OFFSET offset) +{ + BTE_err be; + TPIE_OS_OFFSET new_offset; + TPIE_OS_OFFSET block_offset; + + // Sorry, we can't truncate a substream. + if (substream_level) { + return BTE_ERROR_STREAM_IS_SUBSTREAM; + } + + if (offset < 0) { + return BTE_ERROR_OFFSET_OUT_OF_RANGE; + } + // Compute the new offset + new_offset = + item_off_to_file_off (file_off_to_item_off (f_bos) + offset); + + // If it is not in the same block as the current position then + // invalidate the current block. + + // We also need to check that we have the correct block mapped in (f_offset + // does not always point into the current block!) - see comment in seek() + + if (((char *) current - (char *) curr_block >= header->block_size) || + (((new_offset - os_block_size_) / header->block_size) != + ((f_offset - os_block_size_) / header->block_size))) { +/* + if (block_valid) { + if ((be = invalidate_current ()) != BTE_ERROR_NO_ERROR) +*/ + if (block_valid && ((be = unmap_current()) != BTE_ERROR_NO_ERROR)) { + return be; + } + } + // If it is not in the same block as the current end of stream + // then truncate the file to the end of the new last block. + if (((new_offset - os_block_size_) / header->block_size) != + ((f_eos - os_block_size_) / header->block_size)) { + + if(block_mapped) { + unmap_current(); + } + + // Determine the offset of the block that new_offset is in. + block_offset = BLOCK_OFFSET (new_offset); + // Rajiv + // ((new_offset - os_block_size_) / header->block_size) + // * header->block_size + os_block_size_; + f_filelen = block_offset + header->block_size; + if (TPIE_OS_FTRUNCATE (fd, f_filelen)) { + os_errno = errno; + TP_LOG_FATAL ("Failed to ftruncate() to the new end of \""); + TP_LOG_FATAL (path); + TP_LOG_FATAL ("\": "); + TP_LOG_FATAL (strerror (os_errno)); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + return BTE_ERROR_OS_ERROR; + } + } + // Reset the current position to the end. + f_offset = f_eos = new_offset; + + return BTE_ERROR_NO_ERROR; +} + +// Map in the header from the file. This assumes that the path +// has been cached in path and that the file has been opened and +// fd contains a valid descriptor. +template < class T > +BTE_stream_header * BTE_stream_mmap < T >::map_header (void) { + + TPIE_OS_OFFSET file_end; + BTE_stream_header *mmap_hdr; + + // If the underlying file is not at least long enough to contain + // the header block, then, assuming the stream is writable, we have + // to create the space on disk by doing an explicit write(). + if ((file_end = TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_END)) < os_block_size_) { + if (r_only) { + status_ = BTE_STREAM_STATUS_INVALID; + + TP_LOG_FATAL ("No header block in read only stream \""); + TP_LOG_FATAL (path); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + return NULL; + + } else { + // A writable stream, so we can ftruncate() space for a + // header block. + if (TPIE_OS_FTRUNCATE (fd, os_block_size_)) { + os_errno = errno; + TP_LOG_FATAL ("Failed to ftruncate() to end of header of \""); + TP_LOG_FATAL (path); + TP_LOG_FATAL ("\": "); + TP_LOG_FATAL (strerror (os_errno)); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + return NULL; + } + } + } + + // Map in the header block. If the stream is writable, the header + // block should be too. + // took out the SYSTYPE_BSD ifdef for convenience + // changed from MAP_FIXED to MAP_VARIABLE because we are using NULL + mmap_hdr = (BTE_stream_header *) + (call_mmap ((NULL), sizeof (BTE_stream_header), + r_only, w_only, fd, 0, 0)); + if (mmap_hdr == (BTE_stream_header *) (-1)) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + log_fatal ("mmap() failed to map in header from \"", + path, "\": ", strerror (os_errno), "\n"); + return NULL; + } + + header = (BTE_stream_header *) malloc (sizeof (BTE_stream_header)); + if (!header) { + TP_LOG_FATAL ("out of virtual memory"); + return NULL; + } + memcpy (header, mmap_hdr, sizeof (BTE_stream_header)); + call_munmap (mmap_hdr, sizeof (BTE_stream_header)); + + return header; +} + +// Map in the header from the file. This assumes that the path +// has been cached in path and that the file has been opened and +// fd contains a valid descriptor. +template < class T > void BTE_stream_mmap < T >::unmap_header () +{ + BTE_stream_header *mmap_hdr; + TPIE_OS_OFFSET file_end; + + // If the underlying file is not at least long enough to contain + // the header block, then, assuming the stream is writable, we have + // to create the space on disk by doing an explicit write(). + if ((file_end = TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_END)) < os_block_size_) { + if (r_only) { + status_ = BTE_STREAM_STATUS_INVALID; + + TP_LOG_FATAL ("No header block in read only stream \""); + TP_LOG_FATAL (path); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + return; + + } else { + // A writable stream, so we can ftruncate() space for a + // header block. + if (TPIE_OS_FTRUNCATE (fd, os_block_size_)) { + os_errno = errno; + TP_LOG_FATAL ("Failed to ftruncate() to end of header of \""); + TP_LOG_FATAL (path); + TP_LOG_FATAL ("\": "); + TP_LOG_FATAL (strerror (os_errno)); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + return; + } + } + } + + // Map in the header block. If the stream is writable, the header + // block should be too. + // took out the SYSTYPE_BSD ifdef for convenience + // changed from MAP_FIXED to MAP_VARIABLE because we are using NULL + mmap_hdr = (BTE_stream_header *) + (call_mmap ((NULL), sizeof (BTE_stream_header), + r_only, w_only, fd, 0, 0)); + if (mmap_hdr == (BTE_stream_header *) (-1)) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + log_fatal ("mmap() failed to map in header from \"", + path, "\": ", strerror (os_errno), "\n"); + return; + } + + memcpy (mmap_hdr, header, sizeof (BTE_stream_header)); + call_munmap (mmap_hdr, sizeof (BTE_stream_header)); +} + +// +// Make sure the current block is mapped in and all internal pointers are +// set as appropriate. +// +// + +template < class T > +inline BTE_err BTE_stream_mmap < T >::validate_current (void) +{ + TPIE_OS_SIZE_T block_space; // The space left in the current block. + BTE_err bte_err; + + // If the current block is valid and current points into it and has + // enough room in the block for a full item, we are fine. If it is + // valid but there is not enough room, invalidate it. + if (block_valid) { + assert (current); // sanity check - rajiv + if ((block_space = header->block_size - + ((char *) current - (char *) curr_block)) >= sizeof (T)) { + return BTE_ERROR_NO_ERROR; + } else { // Not enough room left. + // no real need to call invalidate here + // since we call map_current anyway Rajiv + if ((bte_err = unmap_current ()) != BTE_ERROR_NO_ERROR) { + return bte_err; + } + f_offset += block_space; + } + } + // The current block is invalid, since it was either invalid to start + // with or we just invalidated it because we were out of space. + + tp_assert (!block_valid, "Block is already mapped in."); + + // Now map it the block. + bte_err = map_current (); + assert (current); + +#ifdef VERBOSE + // Rajiv + if (verbose && bte_err != BTE_ERROR_NO_ERROR) + cerr << "validate_current failed\n"; +#endif + + // Rajiv + tp_assert (f_offset + sizeof (T) <= f_filelen, + "Advanced too far somehow."); + return bte_err; +} + +// Map in the current block. +// f_offset is used to determine what block is needed. +template < class T > BTE_err BTE_stream_mmap < T >::map_current (void) +{ + TPIE_OS_OFFSET block_offset; + int do_mmap = 0; + BTE_err err; + + // We should not currently have a valid block. + tp_assert (!block_valid, "Block is already mapped in."); + + // Determine the offset of the block that the current item is in. + block_offset = BLOCK_OFFSET (f_offset); + // Rajiv + // - os_block_size_) / header->block_size) + // * header->block_size + os_block_size_; + + // If the block offset is beyond the logical end of the file, then + // we either record this fact and return (if the stream is read + // only) or ftruncate() out to the end of the current block. + assert (TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_END) == f_filelen); + // removed -1 from rhs of comparison below Rajiv + if (f_filelen < block_offset + header->block_size) { + if (r_only) { + //TP_LOG_WARNING_ID("hit eof while reading\n"); + return BTE_ERROR_END_OF_STREAM; + } else { + err = grow_file (block_offset); + if (err != BTE_ERROR_NO_ERROR) + return err; + } + } + // this is what we just fixed. Rajiv + tp_assert (f_offset + sizeof (T) <= f_filelen, + "Advanced too far somehow."); + + // If the current block is already mapped in by this process then + // some systems, (e.g. HP-UX), will not allow us to map it in + // again. This presents all kinds of problems, not only with + // sub/super-stream interactions, which we could probably detect + // by looking back up the path to the level 0 stream, but also + // with overlapping substreams, which are very hard to detect + // since the application can build them however it sees fit. We + // can also have problems if we break a stream into two substreams + // such that their border is in the middle of a block, and then we + // read to the end of the fisrt substream while we are still at + // the beginning of the second. + + // Map it in either r/w or read only. +#ifdef BTE_STREAM_MMAP_READ_AHEAD + if (have_next_block && (block_offset == f_next_block)) { + T *temp; + + temp = curr_block; + curr_block = next_block; + next_block = temp; + have_next_block = 0; +#ifdef COLLECT_STATS + stats_hits++; +#endif + } else { +#ifdef COLLECT_STATS + if (have_next_block) { + // not sequential access + //munmap((caddr_t)next_block, header->block_size); + //have_next_block = 0; + //next_block = NULL; + stats_misses++; + } + stats_compulsory++; +#endif + do_mmap = 1; + } +#else + do_mmap = 1; +#endif + if (do_mmap) { + // took out the SYSTYPE_BSD ifdef for convenience + // MAP_VARIABLE the first time round + // (curr_block ? MAP_FIXED : MAP_VARIABLE) | + if (block_offset + header->block_size > f_filelen) { + grow_file(block_offset); + } + curr_block = (T *) (call_mmap (curr_block, header->block_size, + r_only, w_only, fd, block_offset, + (curr_block != NULL))); + block_mapped = 1; + } + assert ((void *) curr_block != (void *) header); + + if (curr_block == (T *) (-1)) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL ("mmap() failed to map in block at "); + TP_LOG_FATAL (block_offset); + TP_LOG_FATAL (" from \""); + TP_LOG_FATAL (path); + TP_LOG_FATAL ("\": "); + TP_LOG_FATAL (strerror (os_errno)); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + perror ("mmap failed"); // Rajiv + return BTE_ERROR_OS_ERROR; + } + block_valid = 1; + +#ifdef BTE_STREAM_MMAP_READ_AHEAD + // Start the asyncronous read of the next logical block. + read_ahead (); +#endif + + // The offset, in terms of number of items, that current should + // have relative to curr_block. + + register TPIE_OS_OFFSET internal_block_offset; + + internal_block_offset = file_off_to_item_off (f_offset) % + (header->block_size / sizeof (T)); + + current = curr_block + internal_block_offset; + assert (current); + + gstats_.record(BLOCK_READ); + stats_.record(BLOCK_READ); + return BTE_ERROR_NO_ERROR; +} + +template < class T > +inline BTE_err BTE_stream_mmap < T >::invalidate_current (void) +{ + // We should currently have a valid block. + tp_assert (block_valid, "No block is mapped in."); + block_valid = 0; + + return BTE_ERROR_NO_ERROR; +} + +template < class T > BTE_err BTE_stream_mmap < T >::unmap_current (void) { + + // 2003/02/27: Commented this out as it causes test_ami_pmerge to crash. + // (reason: destructor for superstream fails when using substreams) + // Jan. + // invalidate_current (); // not really necessary + + // Unmap it. + if (call_munmap (curr_block, header->block_size)) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + + TP_LOG_FATAL ("munmap() failed to unmap current block"); + TP_LOG_FATAL ("\": "); + TP_LOG_FATAL (strerror (os_errno)); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + return BTE_ERROR_OS_ERROR; + } + curr_block = NULL; // to be safe + block_mapped = 0; + block_valid = 0; + + gstats_.record(BLOCK_WRITE); + stats_.record(BLOCK_WRITE); + return BTE_ERROR_NO_ERROR; +} + +// A uniform method for advancing the current pointer. No mapping, +// unmapping, or anything like that is done here. +template < class T > +inline BTE_err BTE_stream_mmap < T >::advance_current (void) { + + tp_assert (f_offset <= f_filelen, "Advanced too far somehow."); + + // Advance the current pointer and the file offset of the current + // item. + current++; + f_offset += sizeof (T); + + return BTE_ERROR_NO_ERROR; +} + + +// increase the length of the file, to at least +// block_offset + header->block_size +template < class T > inline BTE_err +BTE_stream_mmap < T >::grow_file (TPIE_OS_OFFSET block_offset) +{ + // can't grow substreams (except if called for the + // last substream in a stream. this may happen if map_current + // maps in the last block of a (sub-)stream). + // (tavi) I took this out since ignoreSubstream is not declared... + // assert (ignoreSubstream || !substream_level); + assert(!substream_level); + + f_filelen = block_offset + header->block_size; + if (TPIE_OS_FTRUNCATE (fd, f_filelen) < 0) { + os_errno = errno; + TP_LOG_FATAL ("Failed to ftruncate() out a new block of \""); + TP_LOG_FATAL (path); + TP_LOG_FATAL ("\": "); + TP_LOG_FATAL (strerror (os_errno)); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + // Rajiv + //cerr << "map_current: ftruncate\n"; + return BTE_ERROR_END_OF_STREAM; // generate an error Rajiv + } + assert (TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_END) == f_filelen); + return BTE_ERROR_NO_ERROR; +} + + +template < class T > +TPIE_OS_OFFSET BTE_stream_mmap < T >::item_off_to_file_off (TPIE_OS_OFFSET item_off) const { + TPIE_OS_OFFSET file_off; + + // Move past the header. + + file_off = os_block_size_; + + // Add header->block_size for each full block. + + file_off += header->block_size * + (item_off / (header->block_size / sizeof (T))); + + // Add sizeof(T) for each item in the partially full block. + + file_off += sizeof (T) * (item_off % (header->block_size / sizeof (T))); + + return file_off; +} + +template < class T > +TPIE_OS_OFFSET BTE_stream_mmap < T >::file_off_to_item_off (TPIE_OS_OFFSET file_off) const { + TPIE_OS_OFFSET item_off; + + // Subtract off the header. + file_off -= os_block_size_; + + // Account for the full blocks. + item_off = (header->block_size / sizeof (T)) * + (file_off / header->block_size); + + // Add in the number of items in the last block. + item_off += (file_off % header->block_size) / sizeof (T); + + return item_off; +} + +template < class T > +TPIE_OS_OFFSET BTE_stream_mmap < T >::chunk_size () const { + return header->block_size / sizeof (T); +} + +#ifdef BTE_STREAM_MMAP_READ_AHEAD + +template < class T > +void BTE_stream_mmap < T >::read_ahead () { + + TPIE_OS_OFFSET f_curr_block; + + // The current block had better already be valid or we made a + // mistake in being here. + + tp_assert (block_valid, + "Trying to read ahead when current block is invalid."); + + // Check whether there is a next block. If we are already in the + // last block of the file then it makes no sense to read ahead. + // What if we are writing?? Rajiv + f_curr_block = ((f_offset - os_block_size_) / header->block_size) * + header->block_size + os_block_size_; + + if (f_eos < f_curr_block + 2 * header->block_size) { + return; // XXX + // need to fix this + // if not read only, we can extend the file and prefetch. + // (only if not a substream) + // Rajiv + // prefetch only if write only and not substream + if (!w_only || substream_level) { +#ifdef COLLECT_STATS + stats_eos++; +#endif + return; + } + if (w_only && + !substream_level && + (f_curr_block + 2 * header->block_size > f_filelen)) { +#ifdef VERBOSE + if (verbose) + cout << "growing file (fd" << fd << ") in advance\n"; +#endif + grow_file (f_curr_block); + } + } + + f_next_block = f_curr_block + header->block_size; + + // Rajiv + assert (f_next_block + header->block_size <= f_filelen); + assert (next_block != curr_block); +#if !USE_LIBAIO + // took out the SYSTYPE_BSD ifdef for readability Rajiv + next_block = (T *) (call_mmap (next_block, header->block_size, + r_only, w_only, + fd, f_next_block, (next_block != NULL))); + assert (next_block != (T *) - 1); + have_next_block = 1; +#endif // !USE_LIBAIO + +#if USE_LIBAIO + // Asyncronously read the first word of each os block in the next + // logical block. + for (unsigned int ii = 0; ii < BTE_STREAM_MMAP_BLOCK_FACTOR; ii++) { + + // Make sure there is not a pending request for this block + // before requesting it. + + if (aio_results[ii].aio_return != AIO_INPROGRESS) { + aio_results[ii].aio_return = AIO_INPROGRESS; + + // We have to cancel the last one, even though it completed, + // in order to allow another one with the same result. + aiocancel (aio_results + ii); + + // Start the async I/O. + if (aioread (fd, (char *) (read_ahead_buffer + ii), sizeof (int), + f_next_block + ii * os_block_size_, SEEK_SET, + aio_results + ii)) { + + os_errno = errno; + + TP_LOG_FATAL ("aioread() failed to read ahead"); + TP_LOG_FATAL ("\": "); + TP_LOG_FATAL (strerror (os_errno)); + TP_LOG_FATAL ('\n'); + TP_LOG_FLUSH_LOG; + } + } + } +#endif // USE_LIBAIO +} + +#endif // BTE_STREAM_MMAP_READ_AHEAD + +#undef BTE_STREAM_MMAP_MM_BUFFERS + +#endif // _BTE_STREAM_MMAP_H diff --git a/fastlib/u/nvasil/tpie/bte_stream_stdio.h b/fastlib/u/nvasil/tpie/bte_stream_stdio.h new file mode 100644 index 0000000000..6fb2369d9e --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_stream_stdio.h @@ -0,0 +1,635 @@ +// +// File: bte_stream_stdio.h (formerly bte_stdio.h) +// Author: Darren Erik Vengroff +// 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 + +#include +#include +#include +#include +#include +#include + +#include + +// 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::remaining_streams; + using BTE_stream_base::gstats_; + using BTE_stream_base::status_; + using BTE_stream_base::stats_; + using BTE_stream_base::substream_level; + using BTE_stream_base::per; + using BTE_stream_base::r_only; + + public: + using BTE_stream_base::os_block_size; + using BTE_stream_base::check_header; + using BTE_stream_base::init_header; + using BTE_stream_base::register_memory_allocation; + using BTE_stream_base::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 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 +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 +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 diff --git a/fastlib/u/nvasil/tpie/bte_stream_ufs.h b/fastlib/u/nvasil/tpie/bte_stream_ufs.h new file mode 100644 index 0000000000..87abb97107 --- /dev/null +++ b/fastlib/u/nvasil/tpie/bte_stream_ufs.h @@ -0,0 +1,1522 @@ +// +// File: bte_stream_ufs.h (formerly bte_ufs.h) +// Author: Rakesh Barve +// +// $Id: bte_stream_ufs.h,v 1.17 2005/07/07 20:36:12 adanner Exp $ +// +// BTE streams with blocks I/Oed using read()/write(). This particular +// implementation explicitly manages blocks, and only ever maps in one +// block at a time. This relies on the filesystem to do lookahead. It +// is assumed for the purpose of memory calculations that for each +// block used by TPIE, the filesystem uses up another block of the +// same size. +// +// Completely different from the old bte_ufs.h since this does +// blocking like bte_mmb, only it uses read()/write() to do so. + +// TODO: Get rid of or fix the LIBAIO stuff. As it is now it has no +// chance of working, since it uses the static +// BTE_STREAM_UFS_BLOCK_FACTOR, which is no longer the true +// factor. The true block factor is determined dynamically, from the +// header. +// + +#ifndef _BTE_STREAM_UFS_H +#define _BTE_STREAM_UFS_H + +// Get definitions for working with Unix and Windows +#include + +// For header's type field (85 == 'U'). +#define BTE_STREAM_UFS 85 + +//the code for double buffering is not here.. +#define UFS_DOUBLE_BUFFER 0 + +// Either double buffer explicitly using aio or aio can be used Darren +// style or more directly. Using it directly will probably be better, +// but right now that is not supported. (Solaris and Digital/FreeBSD +// use different aio interfaces. +#if BTE_STREAM_UFS_READ_AHEAD +# if !USE_LIBAIO && !UFS_DOUBLE_BUFFER +# error BTE_STREAM_UFS_READ_AHEAD requested, but no double buff mechanism in config. +# endif +# define BTE_STREAM_UFS_MM_BUFFERS 2 +#else +# define BTE_STREAM_UFS_MM_BUFFERS 1 +#endif + +#if UFS_DOUBLE_BUFFER +# error At present explicit DOUBLE BUFFER not supported. +#endif + +// The double buffering mechanism will use lib_aio on Solaris and the +// asynch.h interface on Digital Unix and FreeBSD. Gut feeling is +// that if file access is maintained sequential performance with both +// UFS_DOUBLE_BUFFER and USE_LIBAIO set off is best. + +#if USE_LIBAIO +# if !HAVE_LIBAIO +# error USE_LIBAIO requested, but aio library not in configuration. +# endif +# if UFS_DOUBLE_BUFFER +# error Darren-style USE_LIBAIO requested, but so is DOUBLE BUFFER +# endif +#endif + +// Get the BTE_stream_base class and related definitions. +#include + +// This code makes assertions and logs errors. +#include +#include + +// Define a sensible logical block factor, if not already defined. +#ifndef BTE_STREAM_UFS_BLOCK_FACTOR +# define BTE_STREAM_UFS_BLOCK_FACTOR 8 +#endif + + +// This is a class template for the implementation of a +// BTE stream of objects of type T such that the entire stream +// resides on a single disk. This version maps in only one +// block of the file at a time. The striped_stream class, such +// that it is comprised of several single disk streams, has +// a member function that is a friend of this class. +template < class T > class BTE_stream_ufs: public BTE_stream_base < T > { +private: + + // Descriptor of the mapped file. + TPIE_OS_FILE_DESCRIPTOR fd; + + TPIE_OS_SIZE_T os_block_size_; + + int itemsize_div_blocksize; + + // Offset of the current item in the file. This is the logical + // offset of the item within the file, that is, the place we would + // have to lseek() to in order to read() or write() the item if we + // were using ordinary (i.e. non-mmap()) file access methods. + TPIE_OS_OFFSET f_offset; + + // [tavi 01/27/02] + // This is the position in the file where the pointer is. We can + // save some lseek() calls by maintaining this. + TPIE_OS_OFFSET file_pointer; + + // Offset just past the end of the last item in the stream. If this + // is a substream, we can't write here or anywhere beyond. + TPIE_OS_OFFSET f_eos; + + // Beginning of the file. Can't write before here. + TPIE_OS_OFFSET f_bos; + + TPIE_OS_OFFSET f_filelen; + + // A pointer to the mapped in header block for the stream. + BTE_stream_header *header; + + // The current item (mapped in) + T *current; + // A pointer to the beginning of the currently mapped block. + T *curr_block; + + // Non-zero if current points to a valid, mapped in block. + int block_valid; + + // If block_valid is one, then block_dirty is 1 if and only if + // mapped block is dirty; obviously block_dirty is always 0 for + // r_only streams. + int block_dirty; + + // When block_valid is one, this is the Offset of curr_block in the + // underlying Unix file. + TPIE_OS_OFFSET curr_block_file_offset; + + TPIE_OS_SIZE_T blocksize_items; + + // A place to cache OS error values. It is normally set after each + // call to the OS. + int os_errno; + // The file name. + char path[BTE_STREAM_PATH_NAME_LEN]; + +#if UFS_DOUBLE_BUFFER + // for use in double buffering, when one is implemented using + // the aio interface. + T *next_block; // ptr to next block + TPIE_OS_OFFSET f_next_block; // position of next block + int have_next_block; // is next block mapped? + +#endif /* UFS_DOUBLE_BUFFER */ + +#if USE_LIBAIO + // A buffer to read the first word of each OS block in the next logical + // block for read ahead. + int read_ahead_buffer[BTE_STREAM_UFS_BLOCK_FACTOR]; + // Results of asyncronous I/O. + aio_result_t aio_results[BTE_STREAM_UFS_BLOCK_FACTOR]; +#endif /* USE_LIBAIO */ + +#if BTE_STREAM_UFS_READ_AHEAD + // Read ahead into the next logical block. + void read_ahead (void); +#endif + + BTE_stream_header *map_header (void); + + inline BTE_err validate_current (void); + inline BTE_err invalidate_current (void); + + BTE_err map_current (void); + BTE_err unmap_current (void); + + inline BTE_err advance_current (void); + + inline TPIE_OS_OFFSET item_off_to_file_off (TPIE_OS_OFFSET item_off) const; + inline TPIE_OS_OFFSET file_off_to_item_off (TPIE_OS_OFFSET item_off) const; + + protected: + using BTE_stream_base::remaining_streams; + using BTE_stream_base::gstats_; + using BTE_stream_base::status_; + using BTE_stream_base::stats_; + using BTE_stream_base::substream_level; + using BTE_stream_base::per; + using BTE_stream_base::r_only; + + public: + using BTE_stream_base::os_block_size; + using BTE_stream_base::check_header; + using BTE_stream_base::init_header; + + public: + // Constructor. + // [tavi 01/09/02] Careful with the lbf (logical block factor) + // parameter. I introduced it in order to avoid errors when reading + // a stream having a different block factor from the default, but + // this make cause errors in applications. For example, the + // AMI_partition_and merge computes memory requirements of temporary + // streams based on the memory usage of the INPUT stream, However, + // the input stream may have different block size from the temporary + // streams created later. Until these issues are addressed, the + // usage of lbf is discouraged. + BTE_stream_ufs (const char *dev_path, BTE_stream_type st, + TPIE_OS_SIZE_T lbf = BTE_STREAM_UFS_BLOCK_FACTOR); + + // A substream constructor. + BTE_stream_ufs (BTE_stream_ufs * super_stream, + BTE_stream_type st, TPIE_OS_OFFSET sub_begin, TPIE_OS_OFFSET sub_end); + + // 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 () 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); + + // Destructor + ~BTE_stream_ufs (); + + B_INLINE BTE_err read_item (T ** elt); + B_INLINE BTE_err write_item (const T & elt); + + TPIE_OS_OFFSET chunk_size (void) const; +}; + +// This constructor creates a stream whose contents are taken from the +// file whose path is given. +template < class T > +BTE_stream_ufs < T >::BTE_stream_ufs (const char *dev_path, + BTE_stream_type st, + TPIE_OS_SIZE_T lbf) { + + status_ = BTE_STREAM_STATUS_NO_STATUS; + + // Check if we have available streams. Don't decrease the number + // yet, since we may encounter an error. + if (remaining_streams <= 0) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("BTE internal error: cannot open more streams."); + 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 \"" << dev_path << "\" too long."); + return; + } + + strncpy (path, dev_path, BTE_STREAM_PATH_NAME_LEN); + + // Cache the OS block size. + os_block_size_ = os_block_size(); + + // This is a top level stream + substream_level = 0; + + per = PERSIST_PERSISTENT; + block_valid = 0; + block_dirty = 0; + // A field to remember the file offset of mapped in block. + curr_block_file_offset = 0; + curr_block = current = NULL; + f_offset = f_bos = os_block_size_; + // To be on the safe side, set this to -1. It will be set to the + // right value by map_header(), below. + file_pointer = -1; + + // Decrease the number of available streams. + remaining_streams--; + + switch (st) { + case BTE_READ_STREAM: + + r_only = 1; + + // Open the file for reading. + if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(fd = TPIE_OS_OPEN_ORDONLY(path))) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("open() failed to open " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + // [tavi 01/07/02] Commented this out. Just because the file is + // unreadable is no reason to crash. + //assert (0); + return; + } + + header = map_header (); + if (check_header (header) < 0) { + status_ = BTE_STREAM_STATUS_INVALID; + return; + } + // Some more checking, specific to this stream type. + if (header->type != BTE_STREAM_UFS) { + TP_LOG_WARNING_ID("Using UFS stream implem. on another type of stream."); + TP_LOG_WARNING_ID("Stream implementations may not be compatible."); + } + if ((header->block_size % os_block_size_ != 0) || + (header->block_size == 0)) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("header: incorrect logical block size;"); + TP_LOG_FATAL_ID ("expected multiple of OS block size."); + return; + } + if (header->block_size != BTE_STREAM_UFS_BLOCK_FACTOR * os_block_size_) { + TP_LOG_WARNING_ID("Stream has different block factor than the default."); + TP_LOG_WARNING_ID("This may cause problems in some existing applications."); + } + + blocksize_items = header->block_size / sizeof (T); + itemsize_div_blocksize = (header->block_size % sizeof (T) == 0); + + // Set the eos marker appropriately. + f_eos = item_off_to_file_off (header->item_logical_eof); + + if (header->item_logical_eof >= 1) { + if (f_eos - item_off_to_file_off (header->item_logical_eof - 1) - + sizeof (T) > 0) { + // Meaning, 1. sizeof (T) does not divide the logical + // blocksize. 2. the last item in the stream is the last + // item that could have been placed on its logical block + // (so that the valid file offset as far as TPIE goes, is + // the beginning of a new block and so strictly greater + // than the byte offset at which the last item ends). In + // this situation, after reading the last item and f_offset + // gets incremented, it is strictly less than f_eos; as a + // result the check (f_eos <= f_offset)? in ::read_item() + // gets beaten when it shouldn't. To remedy, we simply + // reset f_eos in this circumstance to be just past the + // last item's byte offset. + + f_eos = item_off_to_file_off (header->item_logical_eof - 1) + + sizeof (T); + } + } + break; + + case BTE_WRITE_STREAM: + case BTE_WRITEONLY_STREAM: + case BTE_APPEND_STREAM: + + r_only = 0; + + // Open the file for writing. First we will try to open + // is 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(fd = TPIE_OS_OPEN_OEXCL(path))) { + + // Try again, hoping the file already exists. + if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(fd = TPIE_OS_OPEN_ORDWR(path))) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("open() failed to open " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + return; + } + // The file already exists, so read the header. + header = map_header (); + if (check_header (header) < 0) { + status_ = BTE_STREAM_STATUS_INVALID; + return; + } + // Some more checking, specific to this stream. + if (header->type != BTE_STREAM_UFS) { + TP_LOG_WARNING_ID("Using UFS stream implem. on another type of stream."); + TP_LOG_WARNING_ID("Stream implementations may not be compatible."); + } + if ((header->block_size % os_block_size_ != 0) || + (header->block_size == 0)) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID("Header: incorrect logical block size;"); + TP_LOG_FATAL_ID("Expected multiple of OS block size."); + return; + } + if (header->block_size != BTE_STREAM_UFS_BLOCK_FACTOR * os_block_size_) { + TP_LOG_WARNING_ID("Stream has different block factor than the default;"); + TP_LOG_WARNING_ID("\tStream block factor: " << (TPIE_OS_LONGLONG)header->block_size/os_block_size_); + TP_LOG_WARNING_ID("\tDefault block factor: " << BTE_STREAM_UFS_BLOCK_FACTOR); + TP_LOG_WARNING_ID("This may cause problems in some existing applications."); + } + + blocksize_items = header->block_size / sizeof (T); + itemsize_div_blocksize = (header->block_size % sizeof (T) == 0); + + f_eos = item_off_to_file_off (header->item_logical_eof); + + if (header->item_logical_eof >= 1) { + if (f_eos - item_off_to_file_off (header->item_logical_eof - 1) - + sizeof (T) > 0) { + // Meaning, 1. sizeof (T) does not divide the logical + // blocksize. 2. the last item in the stream is the last + // item that could have been placed on its logical block + // (so that the valid file offset as far as TPIE goes, + // is the beginning of a new block and so strictly + // greater than the byte offset at which the last item + // ends). In this situation, after reading the last + // item and f_offset gets incremented, it is strictly + // less than f_eos; as a result the check (f_eos <= + // f_offset)? in ::read_item() gets beaten when it + // shouldn't. To remedy, we simply reset f_eos in this + // circumstance to be just past the last item's byte + // offset. + f_eos = item_off_to_file_off (header->item_logical_eof - 1) + + sizeof (T); + } + } + + if (st == BTE_APPEND_STREAM) { + f_offset = f_eos; + } + + } else { // The file was just created. + + // Create and map in the header. File does not exist, so + // first establish a mapping and then write into the file via + // the mapping. + header = map_header (); + if (header == NULL) { + status_ = BTE_STREAM_STATUS_INVALID; + return; + } + init_header (header); + + if (lbf == 0) { + lbf = 1; + TP_LOG_WARNING_ID("Block factor 0 requested. Using 1 instead."); + } + // Set the logical block size. + header->block_size = lbf * os_block_size_; + // Set the type. + header->type = BTE_STREAM_UFS; + + blocksize_items = header->block_size / sizeof (T); + itemsize_div_blocksize = (header->block_size % sizeof (T) == 0); + + f_eos = os_block_size_; + gstats_.record(STREAM_CREATE); + stats_.record(STREAM_CREATE); + } + + break; + } // end of switch + + // We can't handle streams of large objects. + if (sizeof (T) > header->block_size) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("Object is too big (object size/block size):"); + TP_LOG_FATAL_ID (sizeof(T)); + TP_LOG_FATAL_ID ((TPIE_OS_LONGLONG)header->block_size); + return; + } + +#if UFS_DOUBLE_BUFFER + next_block = NULL; + f_next_block = 0; + have_next_block = 0; +#endif + + + // Memory-usage for the object, base class, header and the stream buffers + // are registered automatically by Darren's modified new() function. + f_filelen = TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_END); + file_pointer = f_filelen; + gstats_.record(STREAM_OPEN); + stats_.record(STREAM_OPEN); +} + + +// A substream constructor. +// sub_begin is the item offset of the first item in the stream. +// sub_end is the item offset that of the last item in the stream. +// Thus, f_eos in the new substream will be set to point one item beyond +// this. +// +// For example, if a stream contains [A,B,C,D,...] then substream(1,3) +// will contain [B,C,D]. +template < class T > +BTE_stream_ufs < T >::BTE_stream_ufs (BTE_stream_ufs * super_stream, + BTE_stream_type st, + TPIE_OS_OFFSET sub_begin, TPIE_OS_OFFSET sub_end) { + + status_ = BTE_STREAM_STATUS_NO_STATUS; + + // Reduce the number of streams avaialble. + if (remaining_streams <= 0) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("BTE error: cannot open more streams."); + return; + } + + if (super_stream->status_ == BTE_STREAM_STATUS_INVALID) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("BTE error: super stream is invalid."); + return; + } + + if (super_stream->r_only && (st != BTE_READ_STREAM)) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID + ("BTE error: super stream is read only and substream is not."); + return; + } + + // If you are going to access a substream of a previously created + // (super)stream we want to make sure that the superstream 's + // currently valid block, if any, is committed to the underlying + // Unix file. Note that with memory mapped implementation such a + // "committing" is automatic but in our case we need to keep track + // of such things. + if (!super_stream->r_only && super_stream->block_valid) { + + super_stream->unmap_current (); + + if (super_stream->status_ == BTE_STREAM_STATUS_INVALID) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("BTE internal error: super stream is invalid."); + return; + } + } + + remaining_streams--; + + // Copy the relevant fields from the super_stream. + + r_only = super_stream->r_only; + strncpy (path, super_stream->path, BTE_STREAM_PATH_NAME_LEN); + os_block_size_ = super_stream->os_block_size_; + blocksize_items = super_stream->blocksize_items; + itemsize_div_blocksize = super_stream->itemsize_div_blocksize; + header = super_stream->header; + substream_level = super_stream->substream_level + 1; + + //Each substream should have a local file descriptor + //so file_pointer and fd position match + //Only READ and WRITE streams allowed + switch(st){ + case BTE_READ_STREAM: + fd=TPIE_OS_OPEN_ORDONLY(path); + break; + case BTE_WRITE_STREAM: + //file better exist if super_stream exists + fd=TPIE_OS_OPEN_ORDWR(path); + break; + default: + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("BTE internal error: Invalid subtream type."); + return; + } + + if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(fd)) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("open() failed to open " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + assert(0); + return; + } + + per = PERSIST_PERSISTENT; + + // The arguments sub_start and sub_end are logical item positions + // within the stream. We need to convert them to offsets within + // the stream where items are found. + + TPIE_OS_OFFSET super_item_begin = file_off_to_item_off (super_stream->f_bos); + + f_bos = item_off_to_file_off (super_item_begin + sub_begin); + f_eos = item_off_to_file_off (super_item_begin + sub_end + 1); + + tp_assert (f_bos <= f_eos, "bos beyond eos"); // sanity check + + if (super_item_begin + sub_end + 1 >= 1) { + if (f_eos - item_off_to_file_off (super_item_begin + sub_end) - + sizeof (T) > 0) { + // Meaning, 1. sizeof (T) does not divide the logical + // blocksize. 2. the last item in the stream is the last item + // that could have been placed on its logical block (so that + // the valid file offset as far as TPIE goes, is the beginning + // of a new block and so strictly greater than the byte offset + // at which the last item ends.) In this situation, after + // reading the last item and f_offset gets incremented, it is + // strictly less than f_eos; as a result the check (f_eos <= + // f_offset)? in ::read_item() gets beaten when it shouldn't. + // To remedy, we simply reset f_eos in this circumstance to be + // just past the last item's byte offset. + f_eos = item_off_to_file_off (super_item_begin + sub_end) + + sizeof (T); + } + } + + tp_assert (f_bos <= f_eos, "bos beyond eos"); // sanity check + + f_filelen = super_stream->f_filelen; + + if (f_eos > super_stream->f_eos) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("BTE internal error: reached beyond super stream eof."); + return; + } + + f_offset = f_bos; + file_pointer = -1; // I don't know where the file pointer is. + current = curr_block = NULL; + block_valid = 0; + block_dirty = 0; + curr_block_file_offset = 0; + +#if UFS_DOUBLE_BUFFER + next_block = NULL; + f_next_block = 0; + have_next_block = 0; +#endif + + // Register memory_usage for the object corresp to the substream. + gstats_.record(STREAM_OPEN); + gstats_.record(SUBSTREAM_CREATE); + stats_.record(STREAM_OPEN); + stats_.record(SUBSTREAM_CREATE); +} + +// A psuedo-constructor for substreams. This serves as a wrapper for +// the constructor above in order to get around the fact that one +// cannot have virtual constructors. +template < class T > +BTE_err BTE_stream_ufs < 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."); + + BTE_stream_ufs < T > *sub = + new BTE_stream_ufs < T > (this, st, sub_begin, sub_end); + + *sub_stream = (BTE_stream_base < T > *) sub; + + return BTE_ERROR_NO_ERROR; +} + +template < class T > BTE_stream_ufs < T >::~BTE_stream_ufs (void) { + + // If the stream is already invalid for some reason, then don't + // worry about anything. + if (status_ == BTE_STREAM_STATUS_INVALID) { + TP_LOG_WARNING_ID ("BTE internal error: invalid stream in destructor."); + return; + } + + // Increase the number of streams avaialble. + if (remaining_streams >= 0) { + remaining_streams++; + } + gstats_.record(STREAM_DELETE); + stats_.record(STREAM_DELETE); + + // If this is writable and not a substream, then put the logical + // eos back into the header before unmapping it. + if (!r_only && !substream_level) { + header->item_logical_eof = file_off_to_item_off (f_eos); + } + + // Unmap the current block if necessary. + if (block_valid) { + unmap_current (); + } + + // If this is not a substream then cleanup. + if (!substream_level) { + // If a writeable stream, write back the header. But only if + // the stream is persistent. Otherwise, don't waste time with + // the system calls. + if (!r_only && per != PERSIST_DELETE) { + if (TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_SET) != 0) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("lseek() failed to move past header of " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + // [tavi 01/07/02] Commented this out. Why panic? + //assert (0); + // TODO: Should we really return? If we do, we have memory leaks. + return; + } + if (TPIE_OS_WRITE (fd, (char *) header, sizeof (BTE_stream_header)) + != sizeof (BTE_stream_header)) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("write() failed during stream destruction for " + << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + // [tavi 01/07/02] Commented this out. Why panic? + //assert (0); + // TODO: Should we really return? If we do, we have memory leaks. + return; + } + // Invalidate the cached file pointer. + file_pointer = -1; + } + + if (header) + delete header; + + if (TPIE_OS_CLOSE (fd)) { + os_errno = errno; + TP_LOG_FATAL_ID ("Failed to close() " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + // [tavi 01/07/02] Commented this out. Why panic? + //assert (0); + return; + } + + // If it should not persist, unlink the file. + if (per == PERSIST_DELETE) { + if (r_only) { + TP_LOG_WARNING_ID("PERSIST_DELETE for read-only stream in " << path); + } else { + if (TPIE_OS_UNLINK (path)) { + os_errno = errno; + TP_LOG_WARNING_ID ("unlink failed during destruction of:"); + TP_LOG_WARNING_ID (path); + TP_LOG_WARNING_ID (strerror (os_errno)); + } else { + gstats_.record(STREAM_DELETE); + stats_.record(STREAM_DELETE); + } + } + } + } else { // end of if (!substream_level) + //Each substream has its own file descriptor so close it. + if (TPIE_OS_CLOSE (fd)) { + os_errno = errno; + TP_LOG_FATAL_ID ("Failed to close() substream" << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + return; + } + gstats_.record(SUBSTREAM_DELETE); + stats_.record(SUBSTREAM_DELETE); + } + + if (curr_block) { + delete [] curr_block; // should be vector delete -RW + + // If you really want to be anal about memory calculation + // consistency then if BTE_IMPLICIT_FS_READAHEAD flag is set you + // should register a memory deallocation of header->block_size AT + // THIS POINT of time in code. At present, since we havent + // registered allocation for these ``implicitly read-ahead'' + // blocks, we don't register the dealloc either. + } +#if UFS_DOUBLE_BUFFER + //Have to think this out since if UFS_DOUBLE_BUFFERING is implemented + //there is the possibility that the aio_read for the next block is + //ongoing at the time of the destruction, in which case trying to + //delete next_block may cause a run-time error. Most probably + // the aio read op may have to be suspended if ongoing. + if (next_block) + delete [] next_block; // use vector delete -RW +#endif + + gstats_.record(STREAM_CLOSE); + stats_.record(STREAM_CLOSE); +} + +template < class T > +B_INLINE BTE_err BTE_stream_ufs < T >::read_item (T ** elt) { + + BTE_err bte_err; + + // Make sure we are not currently at the EOS. + if (f_offset >= f_eos) { + tp_assert (f_eos == f_offset, "Can't read past eos."); + return BTE_ERROR_END_OF_STREAM; + } + // Validate the current block. + if ((bte_err = validate_current ()) != BTE_ERROR_NO_ERROR) { + return bte_err; + } + + // Check and make sure that the current pointer points into the + // current block. + tp_assert (((unsigned int) ((char *) current - (char *) curr_block) <= + (unsigned int) (header->block_size - sizeof (T))), + "current is past the end of the current block"); + tp_assert (((char *) current - (char *) curr_block >= 0), + "current is before the begining of the current block"); + + gstats_.record(ITEM_READ); + stats_.record(ITEM_READ); + + // Read + *elt = current; + + // Advance the current pointer. + advance_current (); + + // If we are in a substream, there should be no way for f_current + // to pass f_eos. + tp_assert (!substream_level || (f_offset <= f_eos), + "Got past eos in a substream."); + + return BTE_ERROR_NO_ERROR; +} + +template < class T > +B_INLINE BTE_err BTE_stream_ufs < T >::write_item (const T & elt) { + + BTE_err bte_err; + + // This better be a writable stream. + if (r_only) { + return BTE_ERROR_READ_ONLY; + } + // Make sure we are not currently at the EOS of a substream. + if (substream_level && (f_eos <= f_offset)) { + tp_assert (f_eos == f_offset, "Went too far in a substream."); + return BTE_ERROR_END_OF_STREAM; + } + // Validate the current block. + if ((bte_err = validate_current ()) != BTE_ERROR_NO_ERROR) { + return bte_err; + } + + // Check and make sure that the current pointer points into the current + // block. + tp_assert (((unsigned int) ((char *) current - (char *) curr_block) <= + (unsigned int) (header->block_size - sizeof (T))), + "current is past the end of the current block"); + tp_assert (((char *) current - (char *) curr_block >= 0), + "current is before the begining of the current block"); + + gstats_.record(ITEM_WRITE); + stats_.record(ITEM_WRITE); + + // Write. + *current = elt; + block_dirty = 1; + + // Advance the current pointer. + advance_current (); + + // If we are in a substream, there should be no way for f_current to + // pass f_eos. + tp_assert (!substream_level || (f_offset <= f_eos), + "Got past eos in a substream."); + + // If we moved past eos, then update eos unless we are in a + // substream, in which case EOS will be returned on the next call. + if ((f_offset > f_eos) && !substream_level) { + // disable the assertion below because it is violated when + // the end of a block is reached and the item size does not + // divide the block size completely (so there is some space left) + // tp_assert(f_offset == f_eos + sizeof(T), "Advanced too far somehow."); + f_eos = f_offset; + } + + return BTE_ERROR_NO_ERROR; +} + +// Query memory usage +// Note that in a substream we do not charge for the memory used by +// the header, since it is accounted for in the 0 level superstream. +template < class T > +BTE_err BTE_stream_ufs < T >::main_memory_usage (size_t * usage, + MM_stream_usage + usage_type) +{ + switch (usage_type) { + case MM_STREAM_USAGE_OVERHEAD: + //sizeof(*this) includes base class. + //header is allocated dynamically, but always allocated, + //even for substreams. Don't forget space overhead per + //"new" on (class, base class, header) + *usage = sizeof(*this) + sizeof(BTE_stream_header) + + 3*MM_manager.space_overhead(); + break; + case MM_STREAM_USAGE_BUFFER: + //space used by buffers, when allocated + *usage = BTE_STREAM_UFS_MM_BUFFERS * header->block_size + + MM_manager.space_overhead(); + break; + case MM_STREAM_USAGE_CURRENT: + //overhead + buffers (if in use) + *usage = sizeof(*this) + sizeof(BTE_stream_header) + + 3*MM_manager.space_overhead() + + ((curr_block == NULL) ? 0 : (BTE_STREAM_UFS_MM_BUFFERS * + header->block_size + MM_manager.space_overhead())); + break; + case MM_STREAM_USAGE_MAXIMUM: + case MM_STREAM_USAGE_SUBSTREAM: + *usage = sizeof(*this) + sizeof(BTE_stream_header) + + BTE_STREAM_UFS_MM_BUFFERS * header->block_size + + 4*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_ufs < T >::stream_len (void) const { + return file_off_to_item_off (f_eos) - file_off_to_item_off (f_bos); +}; + +// Return the path name in newly allocated space. +template < class T > +BTE_err BTE_stream_ufs < T >::name (char **stream_name) { + + TPIE_OS_SIZE_T len = (TPIE_OS_SIZE_T)strlen (path); + + tp_assert (len < BTE_STREAM_PATH_NAME_LEN, "Path length is too long."); + + 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_ufs < T >::seek (TPIE_OS_OFFSET offset) { + + BTE_err be; + TPIE_OS_OFFSET new_offset; + + if ((offset < 0) || + (offset > file_off_to_item_off (f_eos) - + file_off_to_item_off (f_bos))) { + TP_LOG_WARNING_ID ("seek() out of range (off/bos/eos)"); + TP_LOG_WARNING_ID (offset); + TP_LOG_WARNING_ID (file_off_to_item_off (f_bos)); + TP_LOG_WARNING_ID (file_off_to_item_off (f_eos)); + return BTE_ERROR_OFFSET_OUT_OF_RANGE; + } + + // Compute the new offset. + new_offset = item_off_to_file_off (file_off_to_item_off (f_bos) + offset); + + if (((size_t) ((char *) current - (char *) curr_block) >= + header->block_size) + || (((new_offset - os_block_size_) / header->block_size) != + ((f_offset - os_block_size_) / header->block_size))) { + if (block_valid && ((be = unmap_current ()) != BTE_ERROR_NO_ERROR)) { + return be; + } + } else { + if (block_valid) { + + // We have to adjust current. + register TPIE_OS_OFFSET internal_block_offset; + + internal_block_offset = file_off_to_item_off (new_offset) % + blocksize_items; + current = curr_block + internal_block_offset; + } + } + + f_offset = new_offset; + + gstats_.record(ITEM_SEEK); + stats_.record(ITEM_SEEK); + return BTE_ERROR_NO_ERROR; +} + +template < class T > +TPIE_OS_OFFSET BTE_stream_ufs < T >::tell() const { + return file_off_to_item_off(f_offset); +} + +// Truncate the stream. +template < class T > +BTE_err BTE_stream_ufs < T >::truncate (TPIE_OS_OFFSET offset) { + + BTE_err be; + TPIE_OS_OFFSET new_offset; + TPIE_OS_OFFSET block_offset; + + // Sorry, we can't truncate a substream. + if (substream_level) { + return BTE_ERROR_STREAM_IS_SUBSTREAM; + } + + if (offset < 0) { + return BTE_ERROR_OFFSET_OUT_OF_RANGE; + } + // Compute the new offset + new_offset = item_off_to_file_off (file_off_to_item_off (f_bos) + offset); + + // If it is not in the same block as the current position then + // invalidate the current block. + // We also need to check that we have the correct block mapped in (f_offset + // does not always point into the current block!) - see comment in seek() + if (((unsigned int) ((char *) current - (char *) curr_block) >= + header->block_size) + || (((new_offset - os_block_size_) / header->block_size) != + ((f_offset - os_block_size_) / header->block_size))) { + if (block_valid && ((be = unmap_current ()) != BTE_ERROR_NO_ERROR)) { + return be; + } + } + // If it is not in the same block as the current end of stream + // then truncate the file to the end of the new last block. + if (((new_offset - os_block_size_) / header->block_size) != + ((f_eos - os_block_size_) / header->block_size)) { + + // Determine the offset of the block that new_offset is in. + block_offset = ((new_offset - os_block_size_) / header->block_size) + * header->block_size + os_block_size_; + f_filelen = block_offset + header->block_size; + if (TPIE_OS_FTRUNCATE (fd, block_offset + header->block_size)) { + os_errno = errno; + TP_LOG_FATAL_ID ("Failed to ftruncate() to the new end of " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + return BTE_ERROR_OS_ERROR; + } + // Invalidate the file pointer. + file_pointer = -1; + } + // Reset the current position to the end. + f_offset = f_eos = new_offset; + + return BTE_ERROR_NO_ERROR; +} + +// Map in the header from the file. This assumes that the path +// has been cached in path and that the file has been opened and +// fd contains a valid descriptor. +template < class T > +BTE_stream_header * BTE_stream_ufs < T >::map_header (void) { + + TPIE_OS_OFFSET file_end; + BTE_stream_header *ptr_to_header; + + // If the underlying file is not at least long enough to contain + // the header block, then, assuming the stream is writable, we have + // to create the space on disk by doing an explicit write(). + if ((file_end = TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_END)) < (TPIE_OS_OFFSET) os_block_size_) { + if (r_only) { + status_ = BTE_STREAM_STATUS_INVALID; + TP_LOG_FATAL_ID ("No header block in read only stream " << path); + return NULL; + } else { + + // A writable stream, but it doesn't have a header block, + // which means the file was just created and we have to leave + // space for the header block at the beginning of the fille. + // In this case we choose simply to allocate space for header + // fields and return a pointer to ufs_stream_header but first + // we write a dummy os_block_size_ sized block at the + // beginning of the file. This will trigger off sequential + // write optimizations that are useful unless non-sequential + // accesses to data are made. + + char *tmp_buffer = new char[os_block_size_]; + + if (file_end != 0) { + if (TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_SET) != 0) { + os_errno = errno; + TP_LOG_FATAL_ID ("Failed to lseek() in stream " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + return NULL; + } + } + + if (TPIE_OS_WRITE (fd, tmp_buffer, os_block_size_) != + (TPIE_OS_SSIZE_T) os_block_size_) { + os_errno = errno; + TP_LOG_FATAL_ID ("Failed to write() in stream " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + return NULL; + } + + delete [] tmp_buffer; // use vector delete -RW + file_pointer = os_block_size_; + + ptr_to_header = new BTE_stream_header; + if (ptr_to_header != NULL) { + return ptr_to_header; + } else { + os_errno = errno; + TP_LOG_FATAL_ID ("Failed to alloc space for header."); + TP_LOG_FATAL_ID (strerror (os_errno)); + status_ = BTE_STREAM_STATUS_INVALID; + return NULL; + } + } + } + + // Instead of mmap() we simply read in the os_block_size_ leading + // bytes of the file, copy the leading sizeof(ufs_stream_header) + // bytes of the os_block_size_ bytes into the ptr_to_header + // structure and return ptr_to_header. Note that even though we + // could have read only the first sizeof(ufs_stream_header) of the + // file we choose not to do so in order to avoid confusing + // sequential prefetcher. + + char *tmp_buffer = new char[os_block_size_]; + + if (TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_SET) != 0) { + os_errno = errno; + TP_LOG_FATAL_ID ("Failed to lseek() in stream " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + return NULL; + } + + if (TPIE_OS_READ (fd, (char *) tmp_buffer, os_block_size_) != + (TPIE_OS_SSIZE_T) os_block_size_) { + os_errno = errno; + TP_LOG_FATAL_ID ("Failed to read() in stream " << path); + TP_LOG_FATAL_ID (strerror (os_errno)); + return NULL; + } + + file_pointer = os_block_size_; + ptr_to_header = new BTE_stream_header; + memcpy(ptr_to_header, tmp_buffer, sizeof(BTE_stream_header)); + delete [] tmp_buffer; // should use vector delete -RW + return ptr_to_header; +} + +// +// Make sure the current block is mapped in and all internal pointers are +// set as appropriate. +// +template < class T > +inline BTE_err BTE_stream_ufs < T >::validate_current (void) { + + unsigned int block_space; // The space left in the current block. + BTE_err bte_err; + + // If the current block is valid and current points into it and has + // enough room in the block for a full item, we are fine. If it is + // valid but there is not enough room, unmap it. + if (block_valid) { + if ((block_space = (unsigned int)header->block_size - + ((char *) current - (char *) curr_block)) >= (unsigned int)sizeof (T)) { + return BTE_ERROR_NO_ERROR; + } else { // Not enough room left. + if ((bte_err = unmap_current ()) != BTE_ERROR_NO_ERROR) { + return bte_err; + } + f_offset += block_space; + } + } + // The current block is invalid, since it was either invalid to start + // with or we just invalidated it because we were out of space. + tp_assert (!block_valid, "Block is already mapped in."); + + // Now map in the block. + return map_current (); + +} + +template < class T > +inline BTE_err BTE_stream_ufs < T >::invalidate_current (void) +{ + // We should currently have a valid block. + tp_assert (block_valid, "No block is mapped in."); + block_valid = 0; + + return BTE_ERROR_NO_ERROR; +} + +// Map in the current block. +// f_offset is used to determine what block is needed. +template < class T > BTE_err BTE_stream_ufs < T >::map_current (void) { + + TPIE_OS_OFFSET block_offset; + int do_mmap = 0; + + // We should not currently have a valid block. + tp_assert (!block_valid, "Block is already mapped in."); + + // Determine the offset of the block that the current item is in. + block_offset = ((f_offset - os_block_size_) / header->block_size) + * header->block_size + os_block_size_; + + // If the block offset is beyond the logical end of the file, then + // we either record this fact and return (if the stream is read + // only) or ftruncate() out to the end of the current block. + if (f_filelen < block_offset + (TPIE_OS_OFFSET) header->block_size) { + if (r_only) { + return BTE_ERROR_END_OF_STREAM; + } else { + + // An assumption here is that !r_only implies that you won't try to read + // items beyond offset block_offset. This is justified because one invariant + // being maintained is that file length is os_block_size_ + an INTEGRAL + // number of Logical Blocks: By this invariant, since lseek returns something + // smaller than block_offset + header->block_size - 1 (meaning that filesize + // is smaller than block_offset + header->block_size), + // + // A consequence of this assumption is that the block being mapped in + // is being written/appended. Now while using mmapped I/O, what this + // means is that we need to first ftruncate() and then map in the requisite + // block. On the other hand, if we are using the read()/write() BTE, we + // simply do nothing: the unmap_current() call executed during + // validate_current() and before map_current() would have ensured that + // we do not overwrite some previously mapped block. + + // Not mapped I/O + // means we assume we are using the read()/write() BTE + // This means we do an unmap_current() in validate_current() + // just before map_current() so there's no danger of overwriting + // a dirty block. + + if (curr_block == NULL) { + curr_block = new T[(sizeof(T)-1+header->block_size)/sizeof(T)]; + + // If you really want to be anal about memory calculation + // consistency then if BTE_IMPLICIT_FS_READAHEAD flag is + // set you should register a memory allocation of + // header->block_size AT THIS POINT of time in code. + } + + block_valid = 1; + curr_block_file_offset = block_offset; + block_dirty = 0; + + register TPIE_OS_OFFSET internal_block_offset; + + internal_block_offset = + file_off_to_item_off (f_offset) % blocksize_items; + + current = curr_block + internal_block_offset; + + return BTE_ERROR_NO_ERROR; + + } + } + // If the current block is already mapped in by this process then + // some systems, (e.g. HP-UX), will not allow us to map it in + // again. This presents all kinds of problems, not only with + // sub/super-stream interactions, which we could probably detect + // by looking back up the path to the level 0 stream, but also + // with overlapping substreams, which are very hard to detect + // since the application can build them however it sees fit. We + // can also have problems if we break a stream into two substreams + // such that their border is in the middle of a block, and then we + // read to the end of the fisrt substream while we are still at + // the beginning of the second. + +#if UFS_DOUBLE_BUFFER + if (have_next_block && (block_offset == f_next_block)) { + T *temp; + + temp = curr_block; + curr_block = next_block; + next_block = temp; + have_next_block = 0; + } else { + do_mmap = 1; + } +#else + do_mmap = 1; +#endif + + if (do_mmap) { + + if (file_pointer == -1 || block_offset != file_pointer) { + if (TPIE_OS_LSEEK(fd, block_offset, TPIE_OS_FLAG_SEEK_SET) != block_offset) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("seek failed in file:"); + TP_LOG_FATAL_ID (path); + TP_LOG_FATAL_ID(strerror(os_errno)); + return BTE_ERROR_OS_ERROR; + } + } + + if (curr_block == NULL) { + + curr_block = new T[(sizeof(T)-1+header->block_size)/sizeof(T)]; + + // If you really want to be anal about memory calculation + // consistency then if BTE_IMPLICIT_FS_READAHEAD flag is set + // you shd register a memory allocation of header->block_size + // AT THIS POINT of time in code. + } + + if (TPIE_OS_READ (fd, (char *) curr_block, header->block_size) != + (TPIE_OS_SSIZE_T) header->block_size) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("read failed in file "); + TP_LOG_FATAL_ID(path); + TP_LOG_FATAL_ID(strerror(os_errno)); + return BTE_ERROR_OS_ERROR; + } + + // Advance file pointer. + file_pointer = block_offset + header->block_size; + } + + + block_valid = 1; + curr_block_file_offset = block_offset; + block_dirty = 0; + +#if BTE_STREAM_UFS_READ_AHEAD + // Start the asyncronous read of the next logical block. + read_ahead (); +#endif + + // The offset, in terms of number of items, that current should + // have relative to curr_block. + register TPIE_OS_OFFSET internal_block_offset; + + internal_block_offset = + file_off_to_item_off (f_offset) % blocksize_items; + + current = curr_block + internal_block_offset; + + gstats_.record(BLOCK_READ); + stats_.record(BLOCK_READ); + return BTE_ERROR_NO_ERROR; +} + +template < class T > +BTE_err BTE_stream_ufs < T >::unmap_current (void) { + + /// TPIE_OS_OFFSET lseek_retval; + + // We should currently have a valid block. + tp_assert (block_valid, "No block is mapped in."); + + if (!r_only && block_dirty) { + + if (file_pointer == -1 || curr_block_file_offset != file_pointer) { + if (TPIE_OS_LSEEK(fd, curr_block_file_offset, TPIE_OS_FLAG_SEEK_SET) != + curr_block_file_offset) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("lseek() failed while unmapping current block."); + TP_LOG_FATAL_ID (strerror(os_errno)); + return BTE_ERROR_OS_ERROR; + } + } + + if (curr_block_file_offset == f_filelen) + f_filelen += header->block_size; + TPIE_OS_OFFSET bla = 0; + if ((bla = TPIE_OS_WRITE (fd, (char *) curr_block, header->block_size)) != + header->block_size) { + status_ = BTE_STREAM_STATUS_INVALID; + os_errno = errno; + TP_LOG_FATAL_ID ("write() failed to unmap current block."); + TP_LOG_FATAL_ID (bla); + TP_LOG_FATAL_ID ((TPIE_OS_OFFSET)header->block_size); + TP_LOG_FATAL_ID (strerror(os_errno)); + return BTE_ERROR_OS_ERROR; + } + // Advance file pointer. + file_pointer = curr_block_file_offset + header->block_size; + } + + block_dirty = 0; + block_valid = 0; + curr_block_file_offset = 0; + + gstats_.record(BLOCK_WRITE); + stats_.record(BLOCK_WRITE); + return BTE_ERROR_NO_ERROR; +} + +// A uniform method for advancing the current pointer. No mapping, +// unmapping, or anything like that is done here. +template < class T > +inline BTE_err BTE_stream_ufs < T >::advance_current (void) { + + // Advance the current pointer and the file offset of the current + // item. + current++; + f_offset += sizeof (T); + + return BTE_ERROR_NO_ERROR; +} + +template < class T > +inline TPIE_OS_OFFSET BTE_stream_ufs < T >::item_off_to_file_off (TPIE_OS_OFFSET item_off) const +{ + TPIE_OS_OFFSET file_off; + + if (!itemsize_div_blocksize) { + + // Move past the header. + file_off = os_block_size_; + + // Add header->block_size for each full block. + file_off += header->block_size * (item_off / blocksize_items); + + // Add sizeof(T) for each item in the partially full block. + file_off += sizeof (T) * (item_off % blocksize_items); + + return file_off; + + } else { + + return (os_block_size_ + item_off * sizeof (T)); + + } +} + +template < class T > +inline TPIE_OS_OFFSET BTE_stream_ufs < T >::file_off_to_item_off (TPIE_OS_OFFSET file_off) const +{ + TPIE_OS_OFFSET item_off; + + if (!itemsize_div_blocksize) { + + // Subtract off the header. + file_off -= os_block_size_; + + // Account for the full blocks. + item_off = blocksize_items * (file_off / header->block_size); + + // Add in the number of items in the last block. + item_off += (file_off % header->block_size) / sizeof (T); + + return item_off; + + } else { + + return (file_off - os_block_size_) / sizeof (T); + + } +} + +template < class T > +TPIE_OS_OFFSET BTE_stream_ufs < T >::chunk_size (void) const { + return blocksize_items; +} + + +#if BTE_STREAM_UFS_READ_AHEAD + +template < class T > void BTE_stream_ufs < T >::read_ahead (void) +{ + TPIE_OS_OFFSET f_curr_block; + + // The current block had better already be valid or we made a + // mistake in being here. + tp_assert (block_valid, + "Trying to read ahead when current block is invalid."); + + // Check whether there is a next block. If we are already in the + // last block of the file then it makes no sense to read ahead. + f_curr_block = ((f_offset - os_block_size_) / header->block_size) * + header->block_size + os_block_size_; + + if (f_eos < f_curr_block + header->block_size) { + return; + } + + f_next_block = f_curr_block + header->block_size; + +#if USE_LIBAIO + // Asyncronously read the first word of each os block in the next + // logical block. + + for (unsigned int ii = 0; ii < BTE_STREAM_UFS_BLOCK_FACTOR; ii++) { + + // Make sure there is not a pending request for this block + // before requesting it. + if (aio_results[ii].aio_return != AIO_INPROGRESS) { + aio_results[ii].aio_return = AIO_INPROGRESS; + + // We have to cancel the last one, even though it completed, + // in order to allow another one with the same result. + aiocancel (aio_results + ii); + + // Start the async I/O. + if (::aioread (fd, (char *) (read_ahead_buffer + ii), sizeof (int), + f_next_block + ii * os_block_size_, TPIE_OS_FLAG_SEEK_SET, + aio_results + ii)) { + + os_errno = errno; + TP_LOG_FATAL_ID ("aioread() failed to read ahead"); + TP_LOG_FATAL_ID (strerror (os_errno)); + } + } + } + +#endif + +#if UFS_DOUBLE_BUFFER +#error Explicit double buffering not supported using read/write BTE +#endif +} + +#endif /* BTE_STREAM_UFS_READ_AHEAD */ + +#undef BTE_STREAM_UFS_MM_BUFFERS + +#endif // _BTE_STREAM_UFS_H diff --git a/fastlib/u/nvasil/tpie/build.py b/fastlib/u/nvasil/tpie/build.py new file mode 100644 index 0000000000..4accba1dd2 --- /dev/null +++ b/fastlib/u/nvasil/tpie/build.py @@ -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") + ); diff --git a/fastlib/u/nvasil/tpie/comparator.h b/fastlib/u/nvasil/tpie/comparator.h new file mode 100644 index 0000000000..453764f22c --- /dev/null +++ b/fastlib/u/nvasil/tpie/comparator.h @@ -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 + + +// 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 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 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 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 , part of STL + +#endif // _COMPARATOR_H diff --git a/fastlib/u/nvasil/tpie/config.h b/fastlib/u/nvasil/tpie/config.h new file mode 100644 index 0000000000..70bb221251 --- /dev/null +++ b/fastlib/u/nvasil/tpie/config.h @@ -0,0 +1,67 @@ +/* include/config.h. Generated by configure. */ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: config.h.in +// Author: Darren Erik Vengroff +// 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 +#elif HAVE_SYS_UNISTD_H +#include +#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 diff --git a/fastlib/u/nvasil/tpie/config.h.in b/fastlib/u/nvasil/tpie/config.h.in new file mode 100644 index 0000000000..570312eb72 --- /dev/null +++ b/fastlib/u/nvasil/tpie/config.h.in @@ -0,0 +1,66 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: config.h.in +// Author: Darren Erik Vengroff +// 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 +#elif HAVE_SYS_UNISTD_H +#include +#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 diff --git a/fastlib/u/nvasil/tpie/cpu_timer.cc b/fastlib/u/nvasil/tpie/cpu_timer.cc new file mode 100644 index 0000000000..ea844a518d --- /dev/null +++ b/fastlib/u/nvasil/tpie/cpu_timer.cc @@ -0,0 +1,90 @@ +// Copyright (c) 1995 Darren Vengroff +// +// File: cpu_timer.cpp +// Author: Darren Vengroff +// Created: 1/11/95 +// + +#include +VERSION(cpu_timer_cpp,"$Id: cpu_timer.cpp,v 1.9 2004/08/17 16:48:50 jan Exp $"); + +#include + +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; +} + + diff --git a/fastlib/u/nvasil/tpie/cpu_timer.h b/fastlib/u/nvasil/tpie/cpu_timer.h new file mode 100644 index 0000000000..6c53f91b64 --- /dev/null +++ b/fastlib/u/nvasil/tpie/cpu_timer.h @@ -0,0 +1,49 @@ +// +// File: cpu_timer.h +// Author: Darren Vengroff +// 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 + +#include +#include + +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 diff --git a/fastlib/u/nvasil/tpie/internal_sort.h b/fastlib/u/nvasil/tpie/internal_sort.h new file mode 100644 index 0000000000..de8c467158 --- /dev/null +++ b/fastlib/u/nvasil/tpie/internal_sort.h @@ -0,0 +1,469 @@ +// Copyright (c) 2005 Andrew Danner +// +// File: internal_sorter.h +// Author: Andrew Danner +// 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 +#include + +// Use our quicksort, or the sort from STL +#ifdef TPIE_USE_STL_SORT +// portability.h includes for us in the case of STL sort +#include //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 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 +Internal_Sorter_Base::~Internal_Sorter_Base(void){ + //In case someone forgot to call deallocate() + if(ItemArray){ + delete [] ItemArray; + ItemArray=NULL; + } +} + +template +inline void Internal_Sorter_Base::allocate(TPIE_OS_OFFSET nitems){ + len=nitems; + ItemArray = new T[len]; +} + +template +inline void Internal_Sorter_Base::deallocate(void){ + if(ItemArray){ + delete [] ItemArray; + ItemArray=NULL; + len=0; + } +} + +template +inline TPIE_OS_OFFSET Internal_Sorter_Base::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 +inline TPIE_OS_SIZE_T Internal_Sorter_Base::space_overhead(void) +{ + // Space usage independent of space_per_item + // accounts MM_manager space overhead on "new" call + return MM_manager.space_overhead(); +} + +template +inline TPIE_OS_SIZE_T Internal_Sorter_Base::space_per_item(void) +{ + return sizeof(T); +} + +// ********************************************************************* +// * * +// * Operator based Internal Sorter. * +// * * +// ********************************************************************* + +template +class Internal_Sorter_Op: public Internal_Sorter_Base{ + protected: + using Internal_Sorter_Base::len; + using Internal_Sorter_Base::ItemArray; + + public: + //Constructor/Destructor + Internal_Sorter_Op(){}; + ~Internal_Sorter_Op(){}; + + using Internal_Sorter_Base::space_overhead; + + //Sort nItems from input stream and write to output stream + AMI_err sort(AMI_STREAM* InStr, AMI_STREAM* 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 +AMI_err Internal_Sorter_Op::sort(AMI_STREAM* InStr, + AMI_STREAM* 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 (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 Internal_Sorter_Obj: public Internal_Sorter_Base{ + protected: + using Internal_Sorter_Base::ItemArray; + using Internal_Sorter_Base::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::space_overhead; + + //Sort nItems from input stream and write to output stream + AMI_err sort(AMI_STREAM* InStr, AMI_STREAM* 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 +AMI_err Internal_Sorter_Obj::sort(AMI_STREAM* InStr, + AMI_STREAM* 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(cmp_o)); +#else + TP_LOG_DEBUG_ID("calling quick_sort_obj for " << nItems << " items"); + quick_sort_obj (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 Internal_Sorter_KObj{ + protected: + T* ItemArray; //Array that holds original items + qsort_item* 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* InStr, AMI_STREAM* 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 +Internal_Sorter_KObj::~Internal_Sorter_KObj(void){ + //In case someone forgot to call deallocate() + if(ItemArray){ + delete [] ItemArray; + ItemArray=NULL; + } + if(sortItemArray){ + delete [] sortItemArray; + sortItemArray=NULL; + } +} + +template +inline void Internal_Sorter_KObj::allocate(TPIE_OS_OFFSET nitems){ + len=nitems; + ItemArray = new T[len]; + sortItemArray = new qsort_item[len]; +} + +// A helper class to quick sort qsort_item types +// given a comparison object for comparing keys +template +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& left, + const qsort_item& right){ + return isLess->compare(left.keyval, right.keyval); + } +}; + +template +inline AMI_err Internal_Sorter_KObj::sort(AMI_STREAM* InStr, + AMI_STREAM* 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,QsortKeyCmp > + (QsortKeyCmp(UsrObject))); +#else + QsortKeyCmp qcmp(UsrObject); + TP_LOG_DEBUG_ID("calling quick_sort_obj for " << nItems << " items"); + quick_sort_obj< qsort_item > (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 +inline void Internal_Sorter_KObj::deallocate(void){ + len=0; + if(ItemArray){ + delete [] ItemArray; + ItemArray=NULL; + } + if(sortItemArray){ + delete [] sortItemArray; + sortItemArray=NULL; + } +} + +template +inline TPIE_OS_OFFSET Internal_Sorter_KObj::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 +inline TPIE_OS_SIZE_T Internal_Sorter_KObj::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 +inline TPIE_OS_SIZE_T Internal_Sorter_KObj::space_per_item(void) +{ + return sizeof(T) + sizeof(qsort_item); +} + +#endif // _INTERNAL_SORT_H + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fastlib/u/nvasil/tpie/lib_config.h b/fastlib/u/nvasil/tpie/lib_config.h new file mode 100644 index 0000000000..4871e11a0b --- /dev/null +++ b/fastlib/u/nvasil/tpie/lib_config.h @@ -0,0 +1,27 @@ +// +// File: lib_config.h +// Author: Darren Vengroff +// 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 + +// Use logs if requested. +#if TP_LOG_LIB +#define TPL_LOGGING 1 +#endif +#include + +// Enable assertions if requested. +#if TP_ASSERT_LIB +#define DEBUG_ASSERTIONS 1 +#endif +#include + + +#endif // _LIB_CONFIG_H + diff --git a/fastlib/u/nvasil/tpie/logstream.cc b/fastlib/u/nvasil/tpie/logstream.cc new file mode 100644 index 0000000000..9ec868f115 --- /dev/null +++ b/fastlib/u/nvasil/tpie/logstream.cc @@ -0,0 +1,91 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: logstream.cpp +// Author: Darren Erik Vengroff +// Created: 5/12/94 +// +// The logstream class, for writing to the log. +// + +#include +VERSION(logstream_cpp,"$Id: logstream.cpp,v 1.20 2004/08/17 16:48:53 jan Exp $"); + +#include + +// 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 setpriority(unsigned long p) +{ + return logmanip(&manip_priority, p); +} + +logstream& manip_threshold(logstream& tpl, unsigned long p) +{ + tpl.threshold = p; + return tpl; +} + +logmanip setthreshold(unsigned long p) +{ + return logmanip(&manip_threshold, p); +} + + diff --git a/fastlib/u/nvasil/tpie/logstream.h b/fastlib/u/nvasil/tpie/logstream.h new file mode 100644 index 0000000000..1492bc14d5 --- /dev/null +++ b/fastlib/u/nvasil/tpie/logstream.h @@ -0,0 +1,73 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: logstream.h +// Author: Darren Erik Vengroff +// 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 + +// For size_t +#include + +// 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 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& m) { + (*m._f)(o, m._a); + return o; + } +}; + + +logmanip setpriority(unsigned long p); +logmanip setthreshold(unsigned long p); + +#endif // _LOGSTREAM_H diff --git a/fastlib/u/nvasil/tpie/matrix.h b/fastlib/u/nvasil/tpie/matrix.h new file mode 100644 index 0000000000..976aae363e --- /dev/null +++ b/fastlib/u/nvasil/tpie/matrix.h @@ -0,0 +1,922 @@ +// Copyright (c) 1994 Darren Vengroff +// +// File: matrix.h +// Author: Darren Vengroff +// 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 + +#include + +#include + + +// Enable exceptions if the compiler supports them. +#ifndef HANDLE_EXCEPTIONS +#define HANDLE_EXCEPTIONS 0 +#endif + +// References to rows and colums and submatrices. +template class rowref; +template class colref; + +// Matrices and submatrices. +template class matrix_base; +template class matrix; +template class submatrix; + +// A base class for matrices and submatrices. +template 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 row(TPIE_OS_SIZE_T row) ; + colref col(TPIE_OS_SIZE_T col) ; + + rowref operator[](TPIE_OS_SIZE_T row) ; + + // Assignement. + matrix_base &operator=(const matrix_base &rhs); + matrix_base &operator=(const rowref &rhs); + matrix_base &operator=(const colref &rhs); + + // Addition in place. + matrix_base &operator+=(const matrix_base &rhs); +}; + + +// References to rows and columns. +template +class rowref +{ +private: + matrix_base &m; + TPIE_OS_SIZE_T r; +public: + rowref(matrix_base &amatrix, TPIE_OS_SIZE_T row); + ~rowref(void); + + T &operator[](const TPIE_OS_SIZE_T col) const; + + friend class matrix_base; + friend class matrix; +}; + +template +class colref +{ +private: + matrix_base &m; + TPIE_OS_SIZE_T c; +public: + colref(matrix_base &amatrix, TPIE_OS_SIZE_T col); + ~colref(void); + + T &operator[](const TPIE_OS_SIZE_T col) const; + + friend class matrix_base; + friend class matrix; +}; + + +template +matrix_base::matrix_base(TPIE_OS_SIZE_T rows, TPIE_OS_SIZE_T cols) : + r(rows), + c(cols) +{ +} + +template +matrix_base::~matrix_base(void) +{ +} + +template +TPIE_OS_SIZE_T matrix_base::rows(void) const +{ + return r; +} + +template +TPIE_OS_SIZE_T matrix_base::cols(void) const +{ + return c; +} + +template +rowref matrix_base::row(TPIE_OS_SIZE_T row) +{ + if (row >= r) { +#if HANDLE_EXCEPTIONS + throw range(); +#else + tp_assert(0, "Range error."); +#endif + } + + + return rowref(*this, row); +} + +template +colref matrix_base::col(TPIE_OS_SIZE_T col) +{ + if (col >= c) { +#if HANDLE_EXCEPTIONS + throw range(); +#else + tp_assert(0, "Range error."); +#endif + } + + + return colref(*this, col); +} + +template +rowref matrix_base::operator[](TPIE_OS_SIZE_T row) +{ + return this->row(row); +} + + +template +matrix_base &matrix_base::operator=(const matrix_base &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 +matrix_base &matrix_base::operator=(const rowref &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 +matrix_base &matrix_base::operator=(const colref &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 +matrix_base &matrix_base:: + operator+=(const matrix_base &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 +matrix operator+(const matrix_base &op1, + const matrix_base &op2) +{ + if ((op1.rows() != op2.rows()) || (op1.cols() != op2.cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::range(); +#else + tp_assert(0, "Range error."); +#endif + } + + + matrix temp(op1); + + return temp += op2; +} + + +template +void perform_mult_in_place(const matrix_base &op1, + const matrix_base &op2, + matrix_base &res) +{ + if ((op1.cols() != op2.rows()) || + (op1.rows() != res.rows()) || + (op2.cols() != res.cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::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 +void perform_mult_add_in_place(matrix_base &op1, + matrix_base &op2, + matrix_base &res) +{ + if ((op1.cols() != op2.rows()) || + (op1.rows() != res.rows()) || + (op2.cols() != res.cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::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 +matrix operator*(const matrix_base &op1, + const matrix_base &op2) +{ + if (op1.cols() != op2.rows()) { +#if HANDLE_EXCEPTIONS + throw matrix_base::range(); +#else + tp_assert(0, "Range error."); +#endif + } + + matrix temp(op1.rows(),op2.cols()); + + perform_mult_in_place(op1, op2, (matrix_base &)temp); + + return temp; +} + +template +ostream &operator<<(ostream &s, matrix_base &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 +rowref::rowref(matrix_base &amatrix, TPIE_OS_SIZE_T row) : + m(amatrix), + r(row) +{ +} + +template +rowref::~rowref(void) +{ +} + +template +T &rowref::operator[](const TPIE_OS_SIZE_T col) const +{ + return m.elt(r,col); +} + +template +colref::colref(matrix_base &amatrix, TPIE_OS_SIZE_T col) : + m(amatrix), + c(col) +{ +} + +template +colref::~colref(void) +{ +} + +template +T &colref::operator[](const TPIE_OS_SIZE_T row) const +{ + return m.elt(row,c); +} + + +// A submatrix class. +template +class submatrix : public matrix_base +{ +private: + + matrix_base &m; + TPIE_OS_SIZE_T r1,r2,c1,c2; +public: + using matrix_base::rows; + using matrix_base::cols; + + // Construction/destruction. + submatrix(matrix_base &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 &operator=(const submatrix &rhs); + + // We also want to be able to assign from matrices. + submatrix &operator=(const matrix &rhs); + + // Access to elements. + T& elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const; +}; + +template +submatrix::submatrix(matrix_base &amatrix, + TPIE_OS_SIZE_T row1, TPIE_OS_SIZE_T row2, + TPIE_OS_SIZE_T col1, TPIE_OS_SIZE_T col2) : + matrix_base(row2 - row1 + 1, + col2 - col1 + 1), + m(amatrix), + r1(row1), r2(row2), + c1(col1), c2(col2) +{ +} + +template +submatrix::~submatrix(void) +{ +} + +template +submatrix &submatrix::operator=(const submatrix &rhs) +{ + // Call the assignement operator from the base class to do range + // checking and elementwise assignment. + (matrix_base &)(*this) = (matrix_base &)rhs; + + return *this; +} + +template +submatrix &submatrix::operator=(const matrix &rhs) +{ + // Call the assignement operator from the base class to do range + // checking and elementwise assignment. + (matrix_base &)(*this) = (matrix_base &)rhs; + + return *this; +} + +template +T& submatrix::elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const +{ + if ((row >= rows()) || (col >= cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::range(); +#else + tp_assert(0, "Range error."); +#endif + } + return m.elt(row + r1, col + c1); +} + + + +// The matrix class itself. +template +class matrix : public matrix_base { +private: + using matrix_base::r; + using matrix_base::c; + + T *data; +public: + using matrix_base::rows; + using matrix_base::cols; + + // Construction/destruction. + matrix(TPIE_OS_SIZE_T arows, TPIE_OS_SIZE_T acols); + matrix(const matrix &rhs); + matrix(const matrix_base &rhs); + matrix(const submatrix &rhs); + matrix(const rowref umrr); + matrix(const colref 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 &operator=(const matrix &rhs); + + // We also want to be able to assign from submatrices. + matrix &operator=(const submatrix &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 &op1, +// const matrix &op2, +// matrix &res); +// friend void quick_matrix_mult_add_in_place(const matrix &op1, +// const matrix &op2, +// matrix &res); +// friend void aggarwal_matrix_mult_in_place(const matrix &op1, +// const matrix &op2, +// matrix &res); +// friend void aggarwal_matrix_mult_add_in_place(const matrix &op1, +// const matrix &op2, +// matrix &res); + +}; + + +template +matrix::matrix(TPIE_OS_SIZE_T arows, TPIE_OS_SIZE_T acols) : + matrix_base(arows, acols) +{ + data = new T[arows * acols]; + + // Initialize the contents of the matrix. + memset(data, 0, arows * acols * sizeof(T)); +} + +template +matrix::matrix(const matrix &rhs) : + matrix_base(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 +matrix::matrix(const matrix_base &rhs) : + matrix_base(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 &)rhs).elt(ii,jj); + } + } +} + +template +matrix::matrix(const submatrix &rhs) : + matrix_base(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 &)rhs).elt(ii,jj); + } + } +} + +template +matrix::matrix(const rowref umrr) : + matrix_base(1, umrr.m.cols()) +{ + data = new T[c]; + + matrix_base::operator=(umrr); +} + +template +matrix::matrix(const colref umcr) : + matrix_base(umcr.m.rows(),1) +{ + data = new T[r]; + + matrix_base::operator=(umcr); +} + +template +matrix::~matrix(void) { + delete[] data; +} + + +template +matrix &matrix::operator=(const matrix &rhs) +{ + // Call the assignement operator from the base class to do range + // checking and elementwise assignment. + (matrix_base &)(*this) = (matrix_base &)rhs; + + return *this; +} + +template +matrix &matrix::operator=(const submatrix &rhs) +{ + // Call the assignement operator from the base class to do range + // checking and elementwise assignment. + (matrix_base &)(*this) = (matrix_base &)rhs; + + return *this; +} + + +template +T& matrix::elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const +{ + if ((row >= rows()) || (col >= cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::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 \ +matrix operator OP (const TM1 &op1, \ + const TM2 &op2) \ +{ \ + return ((matrix_base &)op1) OP \ + ((matrix_base &)op2); \ +} + +MAT_DUMMY_OP(matrix,matrix,+) +MAT_DUMMY_OP(matrix,submatrix,+) +MAT_DUMMY_OP(submatrix,matrix,+) +MAT_DUMMY_OP(submatrix,submatrix,+) + +MAT_DUMMY_OP(matrix,matrix,*) +MAT_DUMMY_OP(matrix,submatrix,*) +MAT_DUMMY_OP(submatrix,matrix,*) +MAT_DUMMY_OP(submatrix,submatrix,*) + +template +ostream &operator<<(ostream &s, const matrix &m) +{ + return s << (matrix_base &)m; +} + +template +ostream &operator<<(ostream &s, const submatrix &m) +{ + return s << (matrix_base &)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 +void quick_matrix_mult_in_place(const matrix &op1, + const matrix &op2, + matrix &res) +{ + if ((op1.cols() != op2.rows()) || + (op1.rows() != res.rows()) || + (op2.cols() != res.cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::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 +void quick_matrix_mult_add_in_place(const matrix &op1, + const matrix &op2, + matrix &res) +{ + if ((op1.cols() != op2.rows()) || + (op1.rows() != res.rows()) || + (op2.cols() != res.cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::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 +void aggarwal_matrix_mult_in_place(const matrix &op1, + const matrix &op2, + matrix &res) +{ + if ((op1.cols() != op2.rows()) || + (op1.rows() != res.rows()) || + (op2.cols() != res.cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::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 +void aggarwal_matrix_mult_add_in_place(const matrix &op1, + const matrix &op2, + matrix &res) +{ + if ((op1.cols() != op2.rows()) || + (op1.rows() != res.rows()) || + (op2.cols() != res.cols())) { +#if HANDLE_EXCEPTIONS + throw matrix_base::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 diff --git a/fastlib/u/nvasil/tpie/mergeheap.h b/fastlib/u/nvasil/tpie/mergeheap.h new file mode 100644 index 0000000000..99bac418a4 --- /dev/null +++ b/fastlib/u/nvasil/tpie/mergeheap.h @@ -0,0 +1,254 @@ +// +// File: mergeheap.h +// Author: Rakesh Barve +// +// 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 + +#include +#include +#include + +//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 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 merge_heap{ + + merge_heap_element *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 *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 +inline void merge_heap::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 +merge_heap::merge_heap(merge_heap_element *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 merge_heap_cmp { + + class merge_heap_element *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 *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 +inline void merge_heap_cmp::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 +merge_heap_cmp::merge_heap_cmp(class merge_heap_element *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 diff --git a/fastlib/u/nvasil/tpie/mergeheap_dh.h b/fastlib/u/nvasil/tpie/mergeheap_dh.h new file mode 100644 index 0000000000..bb3080570c --- /dev/null +++ b/fastlib/u/nvasil/tpie/mergeheap_dh.h @@ -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 + +// 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 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 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 merge_heap_pdh_op{ + +protected: + + heap_ptr *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); } + + // 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)+MM_manager.space_overhead(); + } + +}; + +template +inline void merge_heap_pdh_op::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 +inline TPIE_OS_SIZE_T merge_heap_pdh_op::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 +inline void merge_heap_pdh_op::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 +inline void merge_heap_pdh_op::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 +inline void merge_heap_pdh_op::allocate ( TPIE_OS_SIZE_T size ) { + Heaparray = new heap_ptr [size+1]; + Heapsize = 0; + maxHeapsize = size; +} + +// Copy an (initial) element into the heap array +template +inline void merge_heap_pdh_op::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 +inline void merge_heap_pdh_op::deallocate () { + if (Heaparray){ + delete [] Heaparray; + Heaparray=NULL; + } + Heapsize = 0; + maxHeapsize = 0; +} + +template +void merge_heap_pdh_op::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 merge_heap_pdh_obj: public merge_heap_pdh_op{ + +protected: + + using merge_heap_pdh_op::Heapsize; + using merge_heap_pdh_op::Heaparray; + using merge_heap_pdh_op::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::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 +inline TPIE_OS_SIZE_T merge_heap_pdh_obj::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 +inline void merge_heap_pdh_obj::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 +inline void merge_heap_pdh_obj::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 +void merge_heap_pdh_obj::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 merge_heap_dh_op{ + +protected: + + heap_element *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); + } + + // 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)+MM_manager.space_overhead(); + } + +}; + +template +inline void merge_heap_dh_op::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 +inline TPIE_OS_SIZE_T merge_heap_dh_op::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 +inline void merge_heap_dh_op::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 +inline void merge_heap_dh_op::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 +inline void merge_heap_dh_op::allocate ( TPIE_OS_SIZE_T size ) { + Heaparray = new heap_element [size+1]; + Heapsize = 0; + maxHeapsize = size; +} + +// Copy an (initial) element into the heap array +template +inline void merge_heap_dh_op::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 +inline void merge_heap_dh_op::deallocate () { + if (Heaparray){ + delete [] Heaparray; + Heaparray=NULL; + } + Heapsize = 0; + maxHeapsize = 0; +}; + +template +void merge_heap_dh_op::initialize () { + for ( TPIE_OS_SIZE_T i = Heapsize/2; i >= 1; i--){ Heapify(i); } +} + + +// ******************************************************************** +// * A merge heap that uses a comparison object * +// ******************************************************************** + +template +class merge_heap_dh_obj: public merge_heap_dh_op{ + +protected: + using merge_heap_dh_op::Heapsize; + using merge_heap_dh_op::Heaparray; + using merge_heap_dh_op::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::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 +inline TPIE_OS_SIZE_T merge_heap_dh_obj::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 +inline void merge_heap_dh_obj::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 +inline void merge_heap_dh_obj::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 +void merge_heap_dh_obj::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 merge_heap_dh_kop{ + +protected: + + heap_element *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); + } + + // 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)+MM_manager.space_overhead(); + } + + // heapify's an initial array of elements + void initialize (void); + +}; + +template +inline void merge_heap_dh_kop::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 +inline void merge_heap_dh_kop::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 +inline void merge_heap_dh_kop::allocate ( TPIE_OS_SIZE_T size ) { + Heaparray = new heap_element [size+1]; + Heapsize = 0; + maxHeapsize = size; +} + +// Copy an (initial) element into the heap array +template +inline void merge_heap_dh_kop::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 +inline void merge_heap_dh_kop::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 +inline TPIE_OS_SIZE_T merge_heap_dh_kop::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 +inline void merge_heap_dh_kop::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 +void merge_heap_dh_kop::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 merge_heap_dh_kobj: public merge_heap_dh_kop{ + +protected: + + using merge_heap_dh_kop::Heapsize; + using merge_heap_dh_kop::Heaparray; + using merge_heap_dh_kop::maxHeapsize; + using merge_heap_dh_kop::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::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(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 +inline TPIE_OS_SIZE_T merge_heap_dh_kobj::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 +inline void merge_heap_dh_kobj::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 +inline void merge_heap_dh_kobj::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 +void merge_heap_dh_kobj::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 diff --git a/fastlib/u/nvasil/tpie/mm.h b/fastlib/u/nvasil/tpie/mm.h new file mode 100644 index 0000000000..31fbf520c9 --- /dev/null +++ b/fastlib/u/nvasil/tpie/mm.h @@ -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 +// 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 + +// Get the base class, enums, etc... +#include + +// Get an implementation definition + +// For now only single address space memory management is supported. +#ifdef MM_IMP_REGISTER +#include +#else +#error No MM implementation selected. +#endif + +#endif // _MM_H diff --git a/fastlib/u/nvasil/tpie/mm_base.cc b/fastlib/u/nvasil/tpie/mm_base.cc new file mode 100644 index 0000000000..e3afad2b97 --- /dev/null +++ b/fastlib/u/nvasil/tpie/mm_base.cc @@ -0,0 +1,160 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: mm_base.cpp +// Author: Darren Erik Vengroff +// Created: 9/2/94 +// + +#include +VERSION(mm_base_cpp,"$Id: mm_base.cpp,v 1.29 2004/10/27 19:13:54 adanner Exp $"); + +#include "lib_config.h" +#include +#include +#include + +#include +#include +#include +#include + +// support for dmalloc (for tracking memory leaks) +#ifdef USE_DMALLOC +#define DMALLOC_DISABLE +#include +#include +#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 + diff --git a/fastlib/u/nvasil/tpie/mm_base.h b/fastlib/u/nvasil/tpie/mm_base.h new file mode 100644 index 0000000000..18dd507a44 --- /dev/null +++ b/fastlib/u/nvasil/tpie/mm_base.h @@ -0,0 +1,71 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: mm_base.h +// Author: Darren Erik Vengroff +// 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 + +#include + +// 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 diff --git a/fastlib/u/nvasil/tpie/mm_register.cc b/fastlib/u/nvasil/tpie/mm_register.cc new file mode 100644 index 0000000000..d5b6dd5352 --- /dev/null +++ b/fastlib/u/nvasil/tpie/mm_register.cc @@ -0,0 +1,240 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: mm_register.cpp +// Author: Darren Erik Vengroff +// Created: 5/31/94 +// + +// A simple registration based memory manager. + +#include +VERSION(mm_register_cpp,"$Id: mm_register.cpp,v 1.23 2005/07/07 20:37:39 adanner Exp $"); + +//#include +#include "lib_config.h" + +#define MM_IMP_REGISTER +#include +#include + +#ifdef REPORT_LARGE_MEMOPS +#include +#endif + +#ifdef MM_BACKWARD_COMPATIBLE +extern int register_new; +#endif + +#include + +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(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(request)); + TP_LOG_MEM_DEBUG("; "); + TP_LOG_MEM_DEBUG(static_cast(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; +} diff --git a/fastlib/u/nvasil/tpie/mm_register.h b/fastlib/u/nvasil/tpie/mm_register.h new file mode 100644 index 0000000000..3688e15768 --- /dev/null +++ b/fastlib/u/nvasil/tpie/mm_register.h @@ -0,0 +1,106 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: mm_register.h +// Author: Darren Erik Vengroff +// 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 + +#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 + + + + + diff --git a/fastlib/u/nvasil/tpie/persist.h b/fastlib/u/nvasil/tpie/persist.h new file mode 100644 index 0000000000..043fa26c26 --- /dev/null +++ b/fastlib/u/nvasil/tpie/persist.h @@ -0,0 +1,28 @@ +// Copyright (c) 1995 Darren Erik Vengroff +// +// File: persist.h +// Author: Darren Erik Vengroff +// 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 + +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 diff --git a/fastlib/u/nvasil/tpie/portability.cc b/fastlib/u/nvasil/tpie/portability.cc new file mode 100644 index 0000000000..5a401b447e --- /dev/null +++ b/fastlib/u/nvasil/tpie/portability.cc @@ -0,0 +1,13 @@ +#include + +//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 diff --git a/fastlib/u/nvasil/tpie/portability.h b/fastlib/u/nvasil/tpie/portability.h new file mode 100644 index 0000000000..63cdf2f664 --- /dev/null +++ b/fastlib/u/nvasil/tpie/portability.h @@ -0,0 +1,1119 @@ +// +// File: portability.h +// Created: 2002/10/30 +// Authors: Joerg Rotthowe, Jan Vahrenhold, Markus Vogel +// +// $Id: portability.h,v 1.32 2005/08/24 19:34:34 adanner Exp $ +// +// This header-file offers macros for independent use on Win and Unix systems. + +#ifndef _PORTABILITY_H +#define _PORTABILITY_H + +// The following wil cause TPIE_OS_SIZE_T to be a 32-bit integer! +#define _TPIE_SMALL_MAIN_MEMORY + +#ifdef _WIN32 +#ifndef __MINGW32__ +#pragma warning (disable : 4018) // signed/unsigned comparison mismatch +#pragma warning (disable : 4786) // debug identifier truncated to 255 chars. +#endif +#endif +// overview of this file: // +////////////////////////////////////////// +// includes // +// typedef, enum, etc. // +// functions // +// non tpie specific // +// tpie specific // +// open functions // +// working with open files // +// close file functions // +// warnings // +// others // + + + +////////////////////////////////////////////// +// includes // +////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +// For time() +#include + + + +// Get random functions // +#include +#include + +#ifdef _WIN32 +#if (_MSC_VER < 1300) && !defined(__MINGW32__) +#include +#else +#include +using namespace std; +#endif +#else +#include +#endif + +// for reading command line parameter // +#if defined(_WIN32) && !defined(__MINGW32__) +#include +#else +#if (__GNUC__ == 2) +#include +#else +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#if (_MSC_VER < 1300) && !defined(__MINGW32__) +using std::ostringstream; +using std::istringstream; +using std::stack; +using std::pair; +using std::less; +using std::lower_bound; +using std::upper_bound; +using std::unique; +using std::sort; +using std::vector; +using std::list; +using std::queue; +using std::priority_queue; +#endif +#else +using namespace std; +#endif + +// Get class tms or time_t // +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef _WIN32 +#include +#else +#define DO_NOTHING +#endif + + +#ifdef _WIN32 +#define DO_NOTHING +#else +#include +#endif + + +#ifdef _WIN32 +# define DO_NOTHING +#else +# if USE_LIBAIO +# include +# endif +#endif + +#ifdef _WIN32 +#define DO_NOTHING +#else +#include +#endif + + +#ifdef _WIN32 +#include +#else +#include +#endif + + +#ifdef _WIN32 +#define DO_NOTHING +#else +#include +#endif + +#include + +// Get functions for mapping // +#ifdef _WIN32 +#include +#else +//extern "C" { +#include +//} +#if !HAVE_PROTOTYPE_MMAP +//extern "C" mmap(caddr_t addr, size_t len, int prot, int flags, +// int filedes, off_t off); +#endif + +#if !HAVE_PROTOTYPE_MUNMAP +//extern "C" int munmap(caddr_t addr, int len); +//extern "C" int madvise(caddr_t addr, int len, int advice); +#endif + +#if !HAVE_PROTOTYPE_FTRUNCATE +//extern "C" int ftruncate(int fd, off_t length); +#endif +#endif + + + + + +////////////////////////////////////////////// +// typedefs, enum etc. // +////////////////////////////////////////////// + +#ifdef _WIN32 +#ifdef _WIN64 +typedef __time64_t TPIE_OS_TIME_T; +inline TPIE_OS_TIME_T TPIE_OS_TIME(TPIE_OS_TIME_T* timep) { + return _time64(timep); +} +#else +typedef time_t TPIE_OS_TIME_T; +inline TPIE_OS_TIME_T TPIE_OS_TIME(TPIE_OS_TIME_T* timep) { + return time(timep); +} +#endif +typedef time_t TPIE_OS_TMS; +#else +typedef time_t TPIE_OS_TIME_T; +typedef tms TPIE_OS_TMS; +inline TPIE_OS_TIME_T TPIE_OS_TIME(TPIE_OS_TIME_T* timep) { + return time(timep); +} +#endif + +#ifdef _WIN32 +typedef LONGLONG TPIE_OS_OFFSET; +#else +typedef off_t TPIE_OS_OFFSET; +#endif + +#ifdef _WIN32 +//windows doesn't have a default way +//of printing 64 bit integers +//printf doesn't work either with %d, use %I64d in Win32 +#if (_MSC_VER < 1300) && !defined(__MINGW32__) +extern ostream& operator<<(ostream& s, const TPIE_OS_OFFSET x); +#endif +#endif + +#if defined (_WIN32) && !defined(__MINGW32__) +typedef long TPIE_OS_LONG; +typedef __int64 TPIE_OS_LONGLONG; +typedef unsigned __int64 TPIE_OS_ULONGLONG; +#else +typedef long TPIE_OS_LONG; +typedef long long int TPIE_OS_LONGLONG; +typedef unsigned long long int TPIE_OS_ULONGLONG; +#endif + +#if defined (_WIN32) && !defined(__MINGW32__) +typedef SSIZE_T TPIE_OS_SSIZE_T; +#ifdef _TPIE_SMALL_MAIN_MEMORY +typedef unsigned __int32 TPIE_OS_SIZE_T; +#define TPIE_OS_OUTPUT_SIZE_T TPIE_OS_SIZE_T +#else +#define TPIE_OS_OUTPUT_SIZE_T TPIE_OS_OFFSET +typedef size_t TPIE_OS_SIZE_T; +#endif +#else +typedef ssize_t TPIE_OS_SSIZE_T; +typedef size_t TPIE_OS_SIZE_T; +#define TPIE_OS_OUTPUT_SIZE_T TPIE_OS_OFFSET +#endif + +#ifdef _WIN32 +enum TPIE_OS_FLAG { + TPIE_OS_FLAG_SEEK_SET = FILE_BEGIN, + TPIE_OS_FLAG_SEEK_CUR = FILE_CURRENT, + TPIE_OS_FLAG_SEEK_END = FILE_END, + TPIE_OS_FLAG_PROT_READ= FILE_MAP_READ, + TPIE_OS_FLAG_PROT_WRITE=FILE_MAP_WRITE, + TPIE_OS_FLAG_MAP_SHARED, + TPIE_OS_FLAG_MS_SYNC, + TPIE_OS_FLAG_MS_ASYNC, + TPIE_OS_FLAG_MAP_FIXED = 0 +}; +#else +enum TPIE_OS_FLAG { + TPIE_OS_FLAG_SEEK_SET = SEEK_SET, + TPIE_OS_FLAG_SEEK_CUR = SEEK_CUR, + TPIE_OS_FLAG_SEEK_END = SEEK_END, + TPIE_OS_FLAG_PROT_READ= PROT_READ, + TPIE_OS_FLAG_PROT_WRITE=PROT_WRITE, + TPIE_OS_FLAG_MAP_SHARED = MAP_SHARED, + TPIE_OS_FLAG_MS_SYNC = MS_SYNC, + TPIE_OS_FLAG_MS_ASYNC = MS_ASYNC, + TPIE_OS_FLAG_MAP_FIXED = MAP_FIXED +}; +#endif + +#ifdef _WIN32 +const int TPIE_OS_PERSIST_READ_ONCE = 0; +#else +const int TPIE_OS_PERSIST_READ_ONCE = 0; +#endif + +enum TPIE_OS_MAPPING_FLAG { + TPIE_OS_FLAG_USE_MAPPING_FALSE, + TPIE_OS_FLAG_USE_MAPPING_TRUE +}; + +#ifdef _WIN32 +typedef struct { + HANDLE FileHandle, + mapFileHandle; + BOOL RDWR; + TPIE_OS_MAPPING_FLAG useFileMapping; +} TPIE_OS_FILE_DESCRIPTOR; +#else +typedef int TPIE_OS_FILE_DESCRIPTOR; +#endif + + +// Default block id type +typedef TPIE_OS_OFFSET TPIE_BLOCK_ID_TYPE; + + +////////////////////////////////////////////// +// macros // +////////////////////////////////////////////// + + +#ifdef _WIN32 +#define TMP_DIR ".\\" +#define TPLOGDIR ".\\" +#define TPIE_OS_TEMPNAMESTR "%s\\%s_XXXXXX" +#else +#define TMP_DIR "/var/tmp/" +#define TPLOGDIR "/tmp/" +#define TPIE_OS_TEMPNAMESTR "%s/%s_XXXXXX" +#endif + + +#ifdef _WIN32 +#define TPIE_OS_STL_STACK stack +#else +#define TPIE_OS_STL_STACK stack +#endif + + +#ifdef _WIN32 +#define TPIE_OS_STL_PAIR pair +#else +#define TPIE_OS_STL_PAIR pair +#endif + +#ifdef _WIN32 +#define TPIE_OS_SET_LIMITS_BODY \ + return 512; +#else +#define TPIE_OS_SET_LIMITS_BODY \ + struct rlimit limits; \ + if(getrlimit(RLIMIT_NOFILE,&limits) == -1) { \ + limits.rlim_cur = 255; \ + } \ + return limits.rlim_cur; +#endif + + +#ifdef _WIN32 +#define TPIE_OS_SET_CLOCK_TICK \ + clock_tick = CLOCKS_PER_SEC +#else +#define TPIE_OS_SET_CLOCK_TICK clock_tick = sysconf(_SC_CLK_TCK); elapsed.tms_utime = 0; elapsed.tms_stime = 0; elapsed.tms_cutime = 0; elapsed.tms_cstime = 0; +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_SET_ELAPSED_TIME(current) +#else +#define TPIE_OS_UNIX_ONLY_SET_ELAPSED_TIME(current) elapsed.tms_utime += (current).tms_utime - last_sync.tms_utime; elapsed.tms_stime += (current).tms_stime - last_sync.tms_stime; elapsed.tms_cutime += (current).tms_cutime - last_sync.tms_cutime; elapsed.tms_cstime += (current).tms_cstime - last_sync.tms_cstime; +#endif + + +#ifdef _WIN32 +#define TPIE_OS_SET_CURRENT_TIME(current) time(& current ); current_real = clock(); +#else +#define TPIE_OS_SET_CURRENT_TIME(current) current_real = times(& current); +#endif + + +#ifdef _WIN32 +#define TPIE_OS_LAST_SYNC_REAL_DECLARATION last_sync_real = clock(); +#else +#define TPIE_OS_LAST_SYNC_REAL_DECLARATION last_sync_real = times(&last_sync); +#endif + + +#ifdef _WIN32 +#define TPIE_OS_USER_TIME_BODY return double(elapsed_real) / double(clock_tick) +#else +#define TPIE_OS_USER_TIME_BODY return double(elapsed.tms_utime) / double(clock_tick) +#endif + + +#ifdef _WIN32 +#define TPIE_OS_OPERATOR_OVERLOAD \ + return s << double(wt.elapsed_real) / double(wt.clock_tick); +#else +#define TPIE_OS_OPERATOR_OVERLOAD return s << double(wt.elapsed.tms_utime) / double(wt.clock_tick) << "u " << double(wt.elapsed.tms_stime) / double(wt.clock_tick) << "s " << double(wt.elapsed_real) / double(wt.clock_tick); +#endif + +// for ANSI conform arrays. +#include + +////////////////////////////////////////////// +// functions // +////////////////////////////////////////////// + + +////////////////////////////////////////////// +// non-tpie specific functions // + +#ifdef _WIN32 +// Generate 31 random bits using rand(), which normally generates only +// 15 random bits. +inline int TPIE_OS_RANDOM() { + return rand() % 0x8000 + (rand() % 0x8000 << 15) + (rand() % 0x2 << 30); +} +#else +inline int TPIE_OS_RANDOM() { + //adanner: rand and srand are ANSI standards + //random and srandom are from old BSD systems + //use the standard unless we run into problems + //http://www.gnu.org/software/libc/manual/html_node/Pseudo_002dRandom-Numbers.html + return rand(); +} +#endif + +#ifdef _WIN32 +inline void TPIE_OS_SRANDOM(unsigned int seed) { + srand(seed); +} +#else +inline void TPIE_OS_SRANDOM(unsigned int seed) { + srand(seed); +} +#endif + +#ifdef _WIN32 +// Win32 File Seeks use high/low order offsets +// Getting Highorder 32 Bit OFFSET +inline LONG getHighOrderOff(TPIE_OS_OFFSET off) { + //Be careful with sign bits. + return (LONG)((ULONGLONG)(off)>>32); +} + + +// Getting Loworder 32 Bit OFFSET +inline LONG getLowOrderOff(TPIE_OS_OFFSET off) { + return (LONG)((ULONGLONG)(off) % 0x000100000000ULL); +} +#endif + + + +////////////////////////////////////////////// +// tpie specific functions // + +#ifdef _WIN32 +inline TPIE_OS_SIZE_T TPIE_OS_PAGESIZE() { + SYSTEM_INFO systemInfos; + GetSystemInfo(&systemInfos); + return (TPIE_OS_SIZE_T )systemInfos.dwPageSize; +} +#else +#ifdef _SC_PAGE_SIZE +inline TPIE_OS_SIZE_T TPIE_OS_PAGESIZE() { + return sysconf (_SC_PAGE_SIZE); +} +#else +inline TPIE_OS_SIZE_T TPIE_OS_PAGESIZE() { + return getpagesize(); +} +#endif +#endif + + +#ifdef _WIN32 +inline TPIE_OS_SIZE_T TPIE_OS_BLOCKSIZE() { + SYSTEM_INFO systemInfos; + GetSystemInfo(&systemInfos); + return systemInfos.dwAllocationGranularity; +} +#else +#ifdef _SC_PAGE_SIZE +inline TPIE_OS_SIZE_T TPIE_OS_BLOCKSIZE() { + return sysconf (_SC_PAGE_SIZE); +} +#else +inline TPIE_OS_SIZE_T TPIE_OS_BLOCKSIZE() { + return getpagesize(); +} +#endif +#endif + + + +////////////////////////////////////////////// +// open functions // + + +//there is no difference between the systemcalls +//but for later adaptation to other systems it maybe useful +#ifdef _WIN32 +inline FILE* TPIE_OS_FOPEN(const char* filename, + const char* mode) { + return fopen(filename,mode); +} +#else +inline FILE* TPIE_OS_FOPEN(const char* filename, + const char* mode) { + return fopen(filename,mode); +} +#endif + +//there is no difference between the systemcalls +//but for later adaptation to other systems it maybe useful +#ifdef _WIN32 +inline int TPIE_OS_FSEEK(FILE* file, TPIE_OS_OFFSET offset, int whence) { + // Please note that the second parameter should be TPIE_OS_OFFSET + // instead of int. This is due to the fact that VS2003 does not + // support large files with fopen/fseek etc. + return fseek(file, static_cast(offset), whence); +} +#else +inline int TPIE_OS_FSEEK(FILE* file, TPIE_OS_OFFSET offset, int whence) { + return fseek(file, offset, whence); +} +#endif + +//there is no difference between the systemcalls +//but for later adaptation to other systems it maybe useful +#ifdef _WIN32 +inline TPIE_OS_LONG TPIE_OS_FTELL(FILE* file) { + return ftell(file); +} +#else +inline TPIE_OS_LONG TPIE_OS_FTELL(FILE* file) { + return ftell(file); +} +#endif + +//there is no difference between the systemcalls +//but for later adaptation to other systems it maybe useful +#ifdef _WIN32 +inline size_t TPIE_OS_FREAD(void* buffer, size_t size, size_t nitems, FILE* stream) { + return fread(buffer, size, nitems, stream); +} +#else +inline size_t TPIE_OS_FREAD(void* buffer, size_t size, size_t nitems, FILE* stream) { + return fread(buffer, size, nitems, stream); +} +#endif + +//there is no difference between the systemcalls +//but for later adaptation to other systems it maybe useful +#ifdef _WIN32 +inline size_t TPIE_OS_FWRITE(const void* buffer, size_t size, size_t nitems, FILE* stream) { + return fwrite(buffer, size, nitems, stream); +} +#else +inline size_t TPIE_OS_FWRITE(const void* buffer, size_t size, size_t nitems, FILE* stream) { + return fwrite(buffer, size, nitems, stream); +} +#endif + +//there is no difference between the systemcalls +//but for later adaptation to other systems it maybe useful +#ifdef _WIN32 +inline int TPIE_OS_FCLOSE(FILE* file) { + return fclose(file); +} +#else +inline int TPIE_OS_FCLOSE(FILE* file) { + return fclose(file); +} +#endif + + +// internal help-function for translating Unix open into a Windows CreateFile + CreateFileMapping +#ifdef _WIN32 +#include +inline TPIE_OS_FILE_DESCRIPTOR portabilityInternalOpen(LPCTSTR name, int flag, TPIE_OS_MAPPING_FLAG mappingFlag) { + TPIE_OS_FILE_DESCRIPTOR internalHandle; + switch(flag) { + case _O_RDONLY: + internalHandle.RDWR = false; + internalHandle.FileHandle = CreateFile( + name, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + 0, 0); + break; + case _O_EXCL: + internalHandle.RDWR = true; + internalHandle.FileHandle = CreateFile( + name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + CREATE_NEW, + 0, 0); + break; + case _O_RDWR: + internalHandle.RDWR = true; + internalHandle.FileHandle = CreateFile( + name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + 0, 0); + break; + default : + internalHandle.RDWR = false; + internalHandle.FileHandle = CreateFile( + name, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + 0, 0); + }; + internalHandle.useFileMapping = mappingFlag; + DWORD dwFileSize = GetFileSize(internalHandle.FileHandle,NULL); + if (dwFileSize == 0) { + SetFilePointer(internalHandle.FileHandle, + static_cast(TPIE_OS_BLOCKSIZE()),0,FILE_BEGIN); + SetEndOfFile(internalHandle.FileHandle); + }; + if (internalHandle.useFileMapping == TPIE_OS_FLAG_USE_MAPPING_TRUE) { + internalHandle.mapFileHandle = + CreateFileMapping( + internalHandle.FileHandle, + 0, + (internalHandle.RDWR ? PAGE_READWRITE : PAGE_READONLY), + 0, 0, + NULL); + } + else { + internalHandle.mapFileHandle = (void*)1; + } + return internalHandle; +} +#endif + + +#ifdef _WIN32 +inline TPIE_OS_FILE_DESCRIPTOR TPIE_OS_OPEN_ORDONLY(const char* name,TPIE_OS_MAPPING_FLAG mappingFlag = TPIE_OS_FLAG_USE_MAPPING_FALSE) { + return portabilityInternalOpen(name, _O_RDONLY,mappingFlag); +} +#else +inline TPIE_OS_FILE_DESCRIPTOR TPIE_OS_OPEN_ORDONLY(const char* name,TPIE_OS_MAPPING_FLAG mappingFlag = TPIE_OS_FLAG_USE_MAPPING_FALSE) { + return ::open(name, O_RDONLY); +} +#endif + + +#ifdef _WIN32 +inline TPIE_OS_FILE_DESCRIPTOR TPIE_OS_OPEN_OEXCL(const char* name, TPIE_OS_MAPPING_FLAG mappingFlag = TPIE_OS_FLAG_USE_MAPPING_FALSE) { + return portabilityInternalOpen(name, _O_EXCL, mappingFlag); +} +#else +inline TPIE_OS_FILE_DESCRIPTOR TPIE_OS_OPEN_OEXCL(const char* name, TPIE_OS_MAPPING_FLAG mappingFlag = TPIE_OS_FLAG_USE_MAPPING_FALSE) { + return ::open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); +} +#endif + + +#ifdef _WIN32 +inline TPIE_OS_FILE_DESCRIPTOR TPIE_OS_OPEN_ORDWR(const char* name, TPIE_OS_MAPPING_FLAG mappingFlag = TPIE_OS_FLAG_USE_MAPPING_FALSE) { + return portabilityInternalOpen(name, _O_RDWR, mappingFlag); +} +#else +inline TPIE_OS_FILE_DESCRIPTOR TPIE_OS_OPEN_ORDWR(const char* name, TPIE_OS_MAPPING_FLAG mappingFlag = TPIE_OS_FLAG_USE_MAPPING_FALSE) { + return ::open(name, O_RDWR); +} +#endif + + +////////////////////////////////////////////// +// working with open files // + +#ifdef _WIN32 +inline BOOL TPIE_OS_IS_VALID_FILE_DESCRIPTOR(TPIE_OS_FILE_DESCRIPTOR& fd) { + BOOL x; + if (fd.FileHandle == INVALID_HANDLE_VALUE || fd.mapFileHandle == NULL) { + x = false; + } else { + x = true; + }; + return x; +} +#else +inline bool TPIE_OS_IS_VALID_FILE_DESCRIPTOR(TPIE_OS_FILE_DESCRIPTOR& fd) { + return (fd == -1 ? false : true); +} +#endif + + +// for working with HANDLEs under Windows we have to use SetFilePointer instead of _lseek // +#ifdef _WIN32 +inline TPIE_OS_OFFSET TPIE_OS_LSEEK(TPIE_OS_FILE_DESCRIPTOR &fd,TPIE_OS_OFFSET offset,TPIE_OS_FLAG origin) { + LONG highOrderOff = getHighOrderOff(offset); + DWORD x = SetFilePointer(fd.FileHandle,getLowOrderOff(offset),&highOrderOff,origin); + + if( x==0xFFFFFFFF && (GetLastError() != NO_ERROR) ){ + //Error + return -1; + } + else{ + return TPIE_OS_OUTPUT_SIZE_T((((ULONGLONG) highOrderOff)<<32)+(ULONGLONG) x); + } +} +#else +inline TPIE_OS_OFFSET TPIE_OS_LSEEK(TPIE_OS_FILE_DESCRIPTOR &fd,TPIE_OS_OFFSET offset,TPIE_OS_FLAG origin) { + return ::lseek(fd, offset, origin); +} +#endif + + +#ifdef _WIN32 +inline TPIE_OS_SSIZE_T TPIE_OS_WRITE(TPIE_OS_FILE_DESCRIPTOR fd, const void* buffer, TPIE_OS_SIZE_T count) { + DWORD bytesWritten = 0; + ::WriteFile(fd.FileHandle, buffer, (DWORD)count, &bytesWritten, 0); + return (TPIE_OS_SSIZE_T)(bytesWritten > 0 ? bytesWritten : -1); +} +#else +inline TPIE_OS_SSIZE_T TPIE_OS_WRITE(TPIE_OS_FILE_DESCRIPTOR fd, const void* buffer, size_t count) { + return ::write(fd,buffer,count); +} +#endif + +#ifdef _WIN32 +inline TPIE_OS_SSIZE_T TPIE_OS_READ(TPIE_OS_FILE_DESCRIPTOR fd, void* buffer, TPIE_OS_SIZE_T count) { + DWORD bytesRead = 0; + ReadFile(fd.FileHandle, buffer, (DWORD)count, &bytesRead, 0); + return (bytesRead > 0 ? bytesRead : -1); +} +#else +inline TPIE_OS_SSIZE_T TPIE_OS_READ(TPIE_OS_FILE_DESCRIPTOR fd, void* buffer, size_t count) { + return ::read(fd,buffer,count); +} +#endif + +#ifdef _WIN32 +// The suggested starting address of the mmap call has to be +// a multiple of the systems granularity (else the mapping fails) +// Hence, the parameter addr is not used at present. +inline LPVOID TPIE_OS_MMAP(LPVOID addr, + size_t len, + int prot, + int flags, + TPIE_OS_FILE_DESCRIPTOR fildes, + TPIE_OS_OFFSET off) { + return MapViewOfFileEx( fildes.mapFileHandle, + prot, + getHighOrderOff(off), + getLowOrderOff(off), + len, NULL); +} +#else +inline void* TPIE_OS_MMAP(void* addr, size_t len, int prot, int flags, TPIE_OS_FILE_DESCRIPTOR fildes, TPIE_OS_OFFSET off) { + return mmap((caddr_t)addr, len, prot, flags, fildes, off); +} +#endif + + +#ifdef _WIN32 +inline int TPIE_OS_MUNMAP(LPVOID addr, size_t len) { + return (UnmapViewOfFile(addr) == 0 ? -1 : 0); +} +#else +inline int TPIE_OS_MUNMAP(void* addr, size_t len) { + return munmap((caddr_t)addr, len); +} +#endif + + +#ifdef _WIN32 +inline int TPIE_OS_MSYNC(LPVOID addr, size_t len, int flags=0) { + return (FlushViewOfFile(addr,len) ? 0 : -1); +} +#else +inline int TPIE_OS_MSYNC(char* addr, size_t len,int flags) { + return msync(addr, len, flags); +} +#endif + + +#ifdef _WIN32 + +// Force the use of truncate to lengthen a collection under WIN32, due +// to mapping issues. +#ifdef BTE_COLLECTION_USE_FTRUNCATE +#undef BTE_COLLECTION_USE_FTRUNCATE +#endif +#define BTE_COLLECTION_USE_FTRUNCATE 1 + +inline int TPIE_OS_FTRUNCATE(TPIE_OS_FILE_DESCRIPTOR& fd, TPIE_OS_OFFSET length) { + // Save the offset + TPIE_OS_OFFSET so = TPIE_OS_LSEEK(fd, 0, TPIE_OS_FLAG_SEEK_CUR); + if (fd.useFileMapping == TPIE_OS_FLAG_USE_MAPPING_TRUE) { + CloseHandle(fd.mapFileHandle); + } + LONG highOrderOff = getHighOrderOff(length); + int x = ((((fd).RDWR == false) || + (SetFilePointer((fd).FileHandle,getLowOrderOff(length),&highOrderOff,FILE_BEGIN) == 0xFFFFFFFF) || + (SetEndOfFile((fd).FileHandle) == 0)) ? -1 : 0); + + if (fd.useFileMapping == TPIE_OS_FLAG_USE_MAPPING_TRUE) { + fd.mapFileHandle= CreateFileMapping( (fd).FileHandle, + 0, + ((fd).RDWR ? PAGE_READWRITE : PAGE_READONLY), + 0, 0, + NULL); + } + // Restore the offset, mimicking the ftruncate() behavior. + TPIE_OS_LSEEK(fd, (so < length ? so : length), TPIE_OS_FLAG_SEEK_SET); + return x; +} +#else +inline int TPIE_OS_FTRUNCATE(TPIE_OS_FILE_DESCRIPTOR& fd, TPIE_OS_OFFSET length) { + return ftruncate(fd, length); +} +#endif + + + +////////////////////////////////////////////// +// tpie close file functions // + + +#ifdef _WIN32 +inline int TPIE_OS_CLOSE(TPIE_OS_FILE_DESCRIPTOR fd) { + if (fd.useFileMapping == TPIE_OS_FLAG_USE_MAPPING_TRUE) { + return (( (CloseHandle(fd.mapFileHandle) != 0) && + (CloseHandle(fd.FileHandle) != 0)) ? 0 : -1); + } else { + return ((CloseHandle(fd.FileHandle) != 0) ? 0 : -1); + } +} +#else +inline int TPIE_OS_CLOSE(TPIE_OS_FILE_DESCRIPTOR fd) { + return ::close(fd); +} +#endif + + +#if defined(_WIN32) && !defined(__MINGW32__) +inline int TPIE_OS_UNLINK(const char* filename) { + return _unlink(filename); +} +#else +inline int TPIE_OS_UNLINK(const char* filename) { + return ::unlink(filename); +} +#endif + + + + + +////////////////////////////////////////////// +// warnings // +////////////////////////////////////////////// + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_AMI_IMP_SINGLE +#else +#define TPIE_OS_UNIX_ONLY_WARNING_AMI_IMP_SINGLE \ +#warning The AMI_IMP_SINGLE flag is obsolete. \ +#warning Please use AMI_STREAM_IMP_SINGLE.\ +#warning Implicitly defining AMI_STREAM_IMP_SINGLE. +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_BTE_COLLECTION_IMP_MMB +#else +#define TPIE_OS_UNIX_ONLY_WARNING_BTE_COLLECTION_IMP_MMB \ +#warning The BTE_COLLECTION_IMP_MMB flag is obsolete.\ +#warning Please use BTE_COLLECTION_IMP_MMAP. \ +#warning Implicitly defining BTE_COLLECTION_IMP_MMAP. +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_MULTIPLE_BTE_COLLECTION_IMP_DEFINED +#else +#define TPIE_OS_UNIX_ONLY_WARNING_MULTIPLE_BTE_COLLECTION_IMP_DEFINED \ +#warning Multiple BTE_COLLECTION_IMP_* defined. \ +#warning Undetermined default implementation. \ +#warning Implicitly defining BTE_COLLECTION_IMP_MMAP. +#endif + + +// To avoid this warning, define one of: BTE_COLLECTION_IMP_MMAP. // +// BTE_COLLECTION_IMP_UFS, BTE_COLLECTION_USER_DEFINED // +// in your app_config.h file. // +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_NO_DEFAULT_BTE_COLLECTION +#else +#define TPIE_OS_UNIX_ONLY_WARNING_NO_DEFAULT_BTE_COLLECTION \ +#warning No default BTE_COLLECTION implementation defined, using BTE_COLLECTION_IMP_MMAP by default. +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_UFS +#else +#define TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_UFS \ +#warning The BTE_IMP_UFS flag is obsolete. Please use BTE_STREAM_IMP_UFS. \ +#warning Implicitly defining BTE_STREAM_IMP_UFS. +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_MMAP +#else +#define TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_MMAP \ +#warning The BTE_IMP_MMB flag is obsolete. Please use BTE_STREAM_IMP_MMAP. \ +#warning Implicitly defining BTE_STREAM_IMP_MMAP. +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_STDIO +#else +#define TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_STDIO \ +#warning The BTE_IMP_STDIO flag is obsolete. Please use BTE_STREAM_IMP_STDIO.\ +#warning Implicitly defining BTE_STREAM_IMP_STDIO. +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_USER_DEFINED +#else +#define TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_USER_DEFINED \ +#warning The BTE_IMP_USER_DEFINED flag is obsolete. Please use BTE_STREAM_IMP_USER_DEFINED.\ +#warning Implicitly defining BTE_STREAM_IMP_USER_DEFINED. +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_MULTIPLE_BTE_STREAM_IMP_DEFINED +#else +#define TPIE_OS_UNIX_ONLY_WARNING_MULTIPLE_BTE_STREAM_IMP_DEFINED \ +#warning Multiple BTE_STREAM_IMP_* defined, but BTE_STREAM_IMP_MULTI_IMP undefined.\ +#warning Implicitly defining BTE_STREAM_IMP_MULTI_IMP. +#endif + + +#ifdef _WIN32 +#define TPIE_OS_UNIX_ONLY_WARNING_NO_IMPLEMENTATION_USING_BTE_STREAM_IMP_UFS +#else +#define TPIE_OS_UNIX_ONLY_WARNING_NO_IMPLEMENTATION_USING_BTE_STREAM_IMP_UFS \ +#warning No implementation defined. Using BTE_STREAM_IMP_UFS by default. +#endif + +#ifdef _WIN32 +inline int TPIE_OS_TRUNCATE(FILE* file, const char* path, TPIE_OS_OFFSET offset) { +// TPIE_OS_LONG highOrderOff = getHighOrderOff(offset); +// DWORD x = SetFilePointer(fd.FileHandle,getLowOrderOff(offset),&highOrderOff, FILE_BEGIN); +// return SetEndOfFile(fd.FileHandle); + // Check for 64-bit file length! (jv) + return _chsize(file->_file, (LONG)offset); +} +#else +inline int TPIE_OS_TRUNCATE(FILE* file, const char* path, TPIE_OS_OFFSET offset) { + return ::truncate(path, offset); +} +#endif + +#ifdef _WIN32 +#define TPIE_OS_TRUNCATE_STREAM_TEMPLATE_CLASS_BODY \ +LOG_FATAL_ID("_WIN32 does not support truncate() for "); \ +LOG_FATAL_ID(path); \ +return BTE_ERROR_OS_ERROR +#else +#define TPIE_OS_TRUNCATE_STREAM_TEMPLATE_CLASS_BODY off_t 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)) { 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 (fseek (file, file_position, SEEK_SET)) { LOG_FATAL("fseek failed to go to position " << file_position << " of \"" << "\"\n"); LOG_FLUSH_LOG; return BTE_ERROR_OS_ERROR; } f_offset = file_position; f_eof = file_position; return BTE_ERROR_NO_ERROR +#endif + +/* +#ifdef _WIN32 +#define TPIE_OS_WIN_ONLY_TEMPLATE_MERGE_HEAP_ELEMENT_COMPILER_FOOLER template<> class merge_heap_element{}; +#else +#define TPIE_OS_WIN_ONLY_TEMPLATE_MERGE_HEAP_ELEMENT_COMPILER_FOOLER +#endif +*/ + + + + +////////////////////////////////////////////// +// others // +////////////////////////////////////////////// + +// config.h needs in line 35: // +// For WIN32, do not include // +// Where is unistd.h? // + + +// #ifndef HAVE_UNISTD_H +// #define HAVE_UNISTD_H 0 +// #endif +// #ifndef HAVE_SYS_UNISTD_H +// #define HAVE_SYS_UNISTD_H 0 +// #endif + +#if defined(_WIN32) && !defined(__MINGW32__) +#define HAVE_UNISTD_H 0 +#define HAVE_SYS_UNISTD_H 0 +#endif + + +// WIN32 does not support data type "long long", but does support "LONGLONG".// +#ifdef _WIN32 +#define TPIE_OS_DECLARE_LOGSTREAM_LONGLONG _DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const LONGLONG); +#else +#define TPIE_OS_DECLARE_LOGSTREAM_LONGLONG _DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const long long); +#endif + +#ifdef _WIN32 +#define TPIE_OS_DEFINE_LOGSTREAM_LONGLONG \ +logstream& logstream::operator<<(const LONGLONG x)\ +{\ + char buf[30];\ + sprintf(buf,"%I64d",x);\ + if (priority <= threshold) {\ + ofstream::operator<<(buf);\ + }\ + return *this;\ +} + +#else +#define TPIE_OS_DEFINE_LOGSTREAM_LONGLONG _DEFINE_LOGSTREAM_OUTPUT_OPERATOR(long long); +#endif + + +#ifdef _WIN32 +#define VERSION(name,id) static char __ ## name[] = id; +#else +#define VERSION(name,id) +// static char __ ## name[] = id; +//#define VERSION(name,id) static char __ ## name[] = ## id; static struct __ ## name ## _compiler_fooler { char *pc; __ ## name ## _compiler_fooler *next; } the__ ## name ## _compiler_fooler = { __ ## name, & the__ ## name ## _compiler_fooler}; +#endif + + + // //** + // *void * operator new() - Get a block of memory from the debug heap + // * + // *Purpose: + // * Allocate of block of memory of at least size bytes from the heap and + // * return a pointer to it. + // * + // * Allocates any type of supported memory block. + // * + // *Entry: + // * unsigned int cb - count of bytes requested + // * int nBlockUse - block type + // * char * szFileName - file name + // * int nLine - line number + // * + // *Exit: + // * Success: Pointer to memory block + // * Failure: NULL (or some error value) + // * + // *Exceptions: + // * + // ******************************************************************************* / + +#ifdef _WIN32 +#ifndef NDEBUG +#define TPIE_OS_SPACE_OVERHEAD_BODY \ +void * __cdecl _nh_malloc_dbg ( size_t, int, int, const char *, int );\ +void * operator new(\ + unsigned int cb,\ + int nBlockUse,\ + const char * szFileName,\ + int nLine\ + )\ +{\ + void *p;\ + if ((MM_manager.register_new != MM_IGNORE_MEMORY_EXCEEDED)\ + && (MM_manager.register_allocation (cb + 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)(cb + SIZE_SPACE));\ + TP_LOG_FATAL(" plus previous allocation ");\ + TP_LOG_FATAL((TPIE_OS_LONG)(MM_manager.memory_used() - (cb + SIZE_SPACE)));\ + TP_LOG_FATAL(" exceeds user-defined limit ");\ + TP_LOG_FATAL((TPIE_OS_LONG)(MM_manager.memory_limit()));\ + TP_LOG_FATAL(" ");\ + cerr << "memory manager: memory allocation limit " << (TPIE_OS_LONG)MM_manager.memory_limit() << " exceeded while allocating " << (TPIE_OS_LONG)cb << " bytes" << endl;\ + exit(1);\ + break;\ + case MM_WARN_ON_MEMORY_EXCEEDED: \ + TP_LOG_WARNING_ID("In operator new() - allocation request \"");\ + TP_LOG_WARNING((TPIE_OS_LONG)(cb + SIZE_SPACE));\ + TP_LOG_WARNING("\" plus previous allocation \"");\ + TP_LOG_WARNING((TPIE_OS_LONG)(MM_manager.memory_used () - (cb + 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)cb << " bytes" << endl;\ + break;\ + case MM_IGNORE_MEMORY_EXCEEDED:\ + break;\ + }\ + }\ + p = malloc (cb+SIZE_SPACE);\ + if (!p) {\ + TP_LOG_FATAL_ID("Out of memory. Cannot continue.");\ + TP_LOG_FLUSH_LOG;\ + cerr << "out of memory while allocating " << (TPIE_OS_LONG)cb << " bytes" << endl;\ + perror ("mm_base::new malloc");\ + assert(0);\ + exit (1);\ + }\ + *((size_t *) p) = cb;\ + return ((char *) p) + SIZE_SPACE;\ +}; +#endif +#else +#define TPIE_OS_SPACE_OVERHEAD_BODY // +#endif + +#endif + +// _portability_H // diff --git a/fastlib/u/nvasil/tpie/pqueue_heap.h b/fastlib/u/nvasil/tpie/pqueue_heap.h new file mode 100644 index 0000000000..44b54afe89 --- /dev/null +++ b/fastlib/u/nvasil/tpie/pqueue_heap.h @@ -0,0 +1,459 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: pqueue_heap.h +// Author: Darren Erik Vengroff +// 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 + +// The virtual base class that defines what priority queues must do. +template +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 +struct q_elt { + T elt; + P priority; +}; + + +// A base class for priority queues that use heaps. + +template +class pqueue_heap +{ +protected: + // A pointer to the array of elements and their priorities. + q_elt * 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 +pqueue_heap::pqueue_heap(unsigned int size) +{ + elements = new q_elt[max_elts = size]; + cur_elts = 0; +} + +template +pqueue_heap::~pqueue_heap() { + delete [] elements; + cur_elts = 0; + max_elts = 0; + return; +} + +template +bool pqueue_heap::full(void) { + return cur_elts == max_elts; +} + +template +unsigned int pqueue_heap::num_elts(void) { + return cur_elts; +} + +template +void pqueue_heap::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 pqueue_heap_op : public pqueue_heap +{ +private: + void heapify(unsigned int root); +protected: + using pqueue_heap::cur_elts; + using pqueue_heap::max_elts; + using pqueue_heap::elements; + +public: + using pqueue_heap::full; + using pqueue_heap::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 +bool pqueue_heap_op::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 +pqueue_heap_op::pqueue_heap_op(unsigned int size) : + pqueue_heap(size) +{ +} + + +template +bool pqueue_heap_op::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 +void pqueue_heap_op::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 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 pqueue_heap_obj : public pqueue_heap +{ +private: + CMPR *cmp_o; + void heapify(unsigned int root); + protected: + using pqueue_heap::cur_elts; + using pqueue_heap::max_elts; + using pqueue_heap::elements; + + public: + using pqueue_heap::full; + using pqueue_heap::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 +bool pqueue_heap_obj::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 +pqueue_heap_obj::pqueue_heap_obj(unsigned int size, CMPR *cmp) + : pqueue_heap(size) + +{ + cmp_o = cmp; +} + + +template +bool pqueue_heap_obj::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 +void pqueue_heap_obj::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 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 pqueue_heap_cmp : public pqueue_heap +{ + + 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::cur_elts; + using pqueue_heap::max_elts; + using pqueue_heap::elements; + public: + using pqueue_heap::full; + using pqueue_heap::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 +bool pqueue_heap_cmp::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 +pqueue_heap_cmp::pqueue_heap_cmp(unsigned int size, + int (*cmp)(const P&, const P&)) : + pqueue_heap(size) { + cmp_f = cmp; +} + + + +template +bool pqueue_heap_cmp::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 +void pqueue_heap_cmp::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 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 diff --git a/fastlib/u/nvasil/tpie/quicksort.h b/fastlib/u/nvasil/tpie/quicksort.h new file mode 100644 index 0000000000..65fbe8708b --- /dev/null +++ b/fastlib/u/nvasil/tpie/quicksort.h @@ -0,0 +1,306 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: quicksort.h +// Author: Darren Erik Vengroff +// 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 + +//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 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 +void partition_op(T *data, size_t len, size_t &partition); + +template +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 +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 +void insertion_sort_op(T *data, size_t len); + +template +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 +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 +void partition_obj(T *data, size_t len, size_t &partition, + CMPR *cmp); + +template +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 +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 +void insertion_sort_obj(T *data, size_t len, + CMPR *cmp); + +template +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 +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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fastlib/u/nvasil/tpie/stdio_stack.h b/fastlib/u/nvasil/tpie/stdio_stack.h new file mode 100644 index 0000000000..602a2b4275 --- /dev/null +++ b/fastlib/u/nvasil/tpie/stdio_stack.h @@ -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 + +#include + +template +class stdio_stack : public BTE_stream_stdio { +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 +stdio_stack::stdio_stack(char *path, + BTE_stream_type type) : + BTE_stream_stdio(path, type) +{ +} + +template +stdio_stack::~stdio_stack(void) +{ +} + +template +BTE_err stdio_stack::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 +BTE_err stdio_stack::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 diff --git a/fastlib/u/nvasil/tpie/timer.h b/fastlib/u/nvasil/tpie/timer.h new file mode 100644 index 0000000000..185a2095e5 --- /dev/null +++ b/fastlib/u/nvasil/tpie/timer.h @@ -0,0 +1,24 @@ +// Copyright (c) 1995 Darren Vengroff +// +// File: timer.h +// Author: Darren Vengroff +// 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 + +class timer { +public: + virtual void start(void) = 0; + virtual void stop(void) = 0; + virtual void reset(void) = 0; +}; + +#endif // _TIMER_H diff --git a/fastlib/u/nvasil/tpie/tpie_assert.h b/fastlib/u/nvasil/tpie/tpie_assert.h new file mode 100644 index 0000000000..07ea34133a --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_assert.h @@ -0,0 +1,36 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: tpie_assert.h +// Author: Darren Erik Vengroff +// 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 + +#include +#include +#include + +#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 + diff --git a/fastlib/u/nvasil/tpie/tpie_log.cc b/fastlib/u/nvasil/tpie/tpie_log.cc new file mode 100644 index 0000000000..ff671fbba8 --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_log.cc @@ -0,0 +1,44 @@ +// +// File: tpie_log.cpp +// Authors: Darren Erik Vengroff +// Octavian Procopiuc +// Created: 5/12/94 +// + +#include +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 +#include +#include +#include + +#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); +} diff --git a/fastlib/u/nvasil/tpie/tpie_log.h b/fastlib/u/nvasil/tpie/tpie_log.h new file mode 100644 index 0000000000..9d5ef2c4f0 --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_log.h @@ -0,0 +1,92 @@ +// Copyright (c) 1994 Darren Erik Vengroff +// +// File: tpie_log.h +// Author: Darren Erik Vengroff +// 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 + +#include + +// 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 diff --git a/fastlib/u/nvasil/tpie/tpie_stats.h b/fastlib/u/nvasil/tpie/tpie_stats.h new file mode 100644 index 0000000000..f808a921be --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_stats.h @@ -0,0 +1,73 @@ +// +// File: tpie_stats.h +// Author: Octavian Procopiuc +// +// $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 + +template +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& 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& 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 +const tpie_stats operator-(const tpie_stats & lhs, + const tpie_stats & rhs) { + tpie_stats res; + for (int i = 0; i < C; i++) + res.stats_[i] = lhs.stats_[i] - rhs.stats_[i]; + return res; +} + +#endif //_TPIE_STATS_H diff --git a/fastlib/u/nvasil/tpie/tpie_stats_coll.h b/fastlib/u/nvasil/tpie/tpie_stats_coll.h new file mode 100644 index 0000000000..f9d0da07b2 --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_stats_coll.h @@ -0,0 +1,33 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: tpie_stats_coll.h +// Authors: Octavian Procopiuc +// +// $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 + +#include + +#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; + +#endif //_TPIE_STATS_COLL_H diff --git a/fastlib/u/nvasil/tpie/tpie_stats_stream.h b/fastlib/u/nvasil/tpie/tpie_stats_stream.h new file mode 100644 index 0000000000..893a6b8670 --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_stats_stream.h @@ -0,0 +1,34 @@ +// +// File: tpie_stats_stream.h +// Authors: Octavian Procopiuc +// +// $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 + +#include + +#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; + +#endif //_TPIE_STATS_STREAM_H diff --git a/fastlib/u/nvasil/tpie/tpie_stats_tree.h b/fastlib/u/nvasil/tpie/tpie_stats_tree.h new file mode 100644 index 0000000000..2db33265fb --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_stats_tree.h @@ -0,0 +1,37 @@ +// Copyright (C) 2001 Octavian Procopiuc +// +// File: tpie_stats_tree.h +// Author: Octavian Procopiuc +// +// $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 + +#include + +#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; + +#endif // _TPIE_STATS_TREE_H diff --git a/fastlib/u/nvasil/tpie/tpie_tempnam.cc b/fastlib/u/nvasil/tpie/tpie_tempnam.cc new file mode 100644 index 0000000000..c0838d6d81 --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_tempnam.cc @@ -0,0 +1,66 @@ +// +// File: tpie_tempnam.cpp +// Author: +// Created: 02/02/02 +// +#include +VERSION(tpie_tempnam_cpp,"$Id: tpie_tempnam.cpp,v 1.6 2004/08/12 12:53:43 jan Exp $"); + +#include +#include +#include +#include +#include "lib_config.h" +#include + +// Defined below. +char *tpie_mktemp(char *str); + +/* like tempnam, but consults environment in an order we like; note + * that the returned pointer is to static storage, so this function is + * not re-entrant. */ +char *tpie_tempnam(const char *base, const char* dir) { + char *base_dir; + static char tmp_path[BUFSIZ]; + char *path; + + if (dir == NULL) { + // get the dir + base_dir = getenv(AMI_SINGLE_DEVICE_ENV); + if (base_dir == NULL) { + base_dir = getenv(TMPDIR_ENV); + if (base_dir == NULL) { + base_dir = TMP_DIR; + } + } + sprintf(tmp_path, TPIE_OS_TEMPNAMESTR, base_dir, base); + } else { + sprintf(tmp_path, TPIE_OS_TEMPNAMESTR, dir, base); + } + + path = tpie_mktemp(tmp_path); + return path; +} + +char *tpie_mktemp(char *str) { + const char chars[] = + { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + const int chars_count = 62; + static TPIE_OS_TIME_T counter = time(NULL) % (chars_count * chars_count); + TPIE_OS_SIZE_T pos = (TPIE_OS_SIZE_T)strlen(str) - 6; + + str[pos++] = chars[counter/chars_count]; + str[pos++] = chars[counter%chars_count]; + + str[pos++] = chars[TPIE_OS_RANDOM() % chars_count]; + str[pos++] = chars[TPIE_OS_RANDOM() % chars_count]; + str[pos++] = chars[TPIE_OS_RANDOM() % chars_count]; + str[pos] = chars[TPIE_OS_RANDOM() % chars_count]; + + counter = (counter + 1) % (chars_count * chars_count); + return str; +} diff --git a/fastlib/u/nvasil/tpie/tpie_tempnam.h b/fastlib/u/nvasil/tpie/tpie_tempnam.h new file mode 100644 index 0000000000..6708001406 --- /dev/null +++ b/fastlib/u/nvasil/tpie/tpie_tempnam.h @@ -0,0 +1,24 @@ +// +// File: tpie_tempnam.h +// Author: +// Created: 02/02/02 +// +// $Id: tpie_tempnam.h,v 1.4 2004/04/16 21:33:29 adanner Exp $ +// +// +#ifndef _TPIE_TEMPNAM_H +#define _TPIE_TEMPNAM_H + +// Get definitions for working with Unix and Windows +#include + +// The name of the environment variable pointing to a tmp directory. +#define TMPDIR_ENV "TMPDIR" + +// The name of the environment variable to consult for default device +// descriptions. +#define AMI_SINGLE_DEVICE_ENV "AMI_SINGLE_DEVICE" + +char *tpie_tempnam(const char *base, const char *dir = NULL); + +#endif // _TPIE_TEMPNAM_H diff --git a/fastlib/u/nvasil/tpie/vararray.h b/fastlib/u/nvasil/tpie/vararray.h new file mode 100644 index 0000000000..671efe9bd4 --- /dev/null +++ b/fastlib/u/nvasil/tpie/vararray.h @@ -0,0 +1,337 @@ +// Copyright (c) 2002 Jan Vahrenhold +// +// File: vararray.h +// Author: Jan Vahrenhold +// Created: 2002/12/02 +// +// Description: Templates classes for one-, two-, and +// three-dimensional arrays. +// +// $Id: vararray.h,v 1.3 2004/11/17 22:31:59 adanner Exp $ +// +#ifndef _VARARRAY_H +#define _VARARRAY_H + +#include + +#include +#include +#include + +//---------------------------------------------------------------------- + +template class VarArray1D { + +public: + // There is no default constructor. + + VarArray1D(TPIE_OS_SIZE_T dim0); + VarArray1D(const VarArray1D& other); + ~VarArray1D(); + + VarArray1D& operator=(const VarArray1D& other); + + const T& operator()(TPIE_OS_SIZE_T index0) const; + T& operator()(TPIE_OS_SIZE_T index0); + + TPIE_OS_SIZE_T size() const; + +protected: + T* data; + TPIE_OS_SIZE_T dim; + +private: + VarArray1D() {} + +}; + +//---------------------------------------------------------------------- + +template class VarArray2D { + +public: + // There is no default constructor. + VarArray2D(TPIE_OS_SIZE_T dim0, TPIE_OS_SIZE_T dim1); + VarArray2D(const VarArray2D& other); + ~VarArray2D(); + + VarArray2D& operator=(const VarArray2D& other); + + const T& operator()(TPIE_OS_SIZE_T index0, TPIE_OS_SIZE_T index1) const; + T& operator()(TPIE_OS_SIZE_T index0, TPIE_OS_SIZE_T index1); + + TPIE_OS_SIZE_T size() const; + TPIE_OS_SIZE_T size(TPIE_OS_SIZE_T d) const; + +protected: + T* data; + TPIE_OS_SIZE_T dim[2]; + +private: + VarArray2D() {} + +}; + +//---------------------------------------------------------------------- + +template class VarArray3D { + +public: + // There is no default constructor. + VarArray3D(TPIE_OS_SIZE_T dim0, TPIE_OS_SIZE_T dim1, TPIE_OS_SIZE_T dim2); + VarArray3D(const VarArray3D& other); + ~VarArray3D(); + + VarArray3D& operator=(const VarArray3D& other); + + const T& operator()(TPIE_OS_SIZE_T index0, TPIE_OS_SIZE_T index1, TPIE_OS_SIZE_T index2) const; + T& operator()(TPIE_OS_SIZE_T index0, TPIE_OS_SIZE_T index1, TPIE_OS_SIZE_T index2); + + TPIE_OS_SIZE_T size() const; + TPIE_OS_SIZE_T size(TPIE_OS_SIZE_T d) const; + +protected: + T* data; + TPIE_OS_SIZE_T dim[3]; + +private: + VarArray3D() {} + +}; + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- + +template +VarArray1D::VarArray1D(TPIE_OS_SIZE_T dim) { + this->dim = dim; + + // Allocate memory for dim0 elements of type/class T. + data = new T[dim]; + + // Initialize memory. + memset((void*)data, + 0, + dim * sizeof(T)); +} + +template +VarArray1D::VarArray1D(const VarArray1D& other) { + *this = other; +} + +template +VarArray1D::~VarArray1D() { + // Free allocated memory. + delete[] data; +} + +template +VarArray1D& VarArray1D::operator=(const VarArray1D& other) { + if (this != &other) { + this->dim = other.dim; + + // Allocate memory for dim elements of type/class T. + data = new T[dim]; + + // Copy objects. + for(int i = 0; i < dim; i++) { + data[i] = other.data[i]; + } + +// // Initialize memory. +// memcpy((void*)(this.data), +// (void*)(other.data), +// dim * sizeof(T)); + + } + return (*this); +} + +template +const T& VarArray1D::operator()(TPIE_OS_SIZE_T index0) const { + assert(index0 < size()); + + return data[index0]; +} + +template +T& VarArray1D::operator()(TPIE_OS_SIZE_T index0) { + assert(index0 < size()); + + return data[index0]; +} + +template +TPIE_OS_SIZE_T VarArray1D::size() const { + return dim; +} + +//---------------------------------------------------------------------- + +template +VarArray2D::VarArray2D(TPIE_OS_SIZE_T dim0, TPIE_OS_SIZE_T dim1) { + this->dim[0] = dim0; + this->dim[1] = dim1; + + // Allocate memory for dim0 * dim1 elements of type/class T. + data = new T[dim0 * dim1]; + + // Initialize memory. + memset((void*)data, + 0, + dim0 * dim1 * sizeof(T)); +} + +template +VarArray2D::VarArray2D(const VarArray2D& other) { + *this = other; +} + +template +VarArray2D::~VarArray2D() { + // Free allocated memory. + delete[] data; +} + +template +VarArray2D& VarArray2D::operator=(const VarArray2D& other) { + if (this != &other) { + this->dim[0] = other.dim[0]; + this->dim[1] = other.dim[1]; + + // Allocate memory for dim0 * dim1 elements of type/class T. + data = new T[dim[0] * dim[1]]; + + // Copy objects. + int len = dim[0] * dim[1]; + for(int i = 0; i < len; i++) { + data[i] = other.data[i]; + } + +// // Initialize memory. +// memcpy((void*)(this.data), +// (void*)(other.data), +// dim[0] * dim[1] * sizeof(T)); + + } + return (*this); +} + +template +T& VarArray2D::operator()(TPIE_OS_SIZE_T index0, TPIE_OS_SIZE_T index1) { + assert(index0 < size(0)); + assert(index1 < size(1)); + + return data[index0 * size(1) + index1]; +} + +template +const T& VarArray2D::operator()(TPIE_OS_SIZE_T index0, TPIE_OS_SIZE_T index1) const { + assert(index0 < size(0)); + assert(index1 < size(1)); + + return data[index0 * size(1) + index1]; +} + +template +TPIE_OS_SIZE_T VarArray2D::size() const { + + return dim[0] * dim[1]; +} + +template +TPIE_OS_SIZE_T VarArray2D::size(TPIE_OS_SIZE_T d) const { + assert(d<2); + + return dim[d]; +} + + +//---------------------------------------------------------------------- + +template +VarArray3D::VarArray3D(TPIE_OS_SIZE_T dim0, TPIE_OS_SIZE_T dim1, TPIE_OS_SIZE_T dim2) { + this->dim[0] = dim0; + this->dim[1] = dim1; + this->dim[2] = dim2; + + // Allocate memory for dim0 * dim1 * dim2 elements of type/class T. + data = new T[dim0 * dim1 * dim2]; + + // Initialize memory. + memset((void*)data, + 0, + dim0 * dim1 * dim2 * sizeof(T)); +} + +template +VarArray3D::VarArray3D(const VarArray3D& other) { + *this = other; +} + +template +VarArray3D::~VarArray3D() { + // Free allocated memory. + delete[] data; +} + +template +VarArray3D& VarArray3D::operator=(const VarArray3D& other) { + if (this != &other) { + this->dim[0] = other.dim[0]; + this->dim[1] = other.dim[1]; + this->dim[2] = other.dim[2]; + + // Allocate memory for dim0 * dim1 * dim2 elements of type/class T. + data = new T[dim[0] * dim[1] * dim[2]]; + + // Copy objects. + int len = dim[0] * dim[1] * dim[2]; + for(int i = 0; i < len; i++) { + data[i] = other.data[i]; + } + +// // Initialize memory. +// memcpy((void*)(this.data), +// (void*)(other.data), +// dim[0] * dim[1] * dim[2] * sizeof(T)); + + } + return (*this); +} + +template +T& VarArray3D::operator()(TPIE_OS_SIZE_T index0, TPIE_OS_SIZE_T index1, TPIE_OS_SIZE_T index2) { + assert(index0 < size(0)); + assert(index1 < size(1)); + assert(index2 < size(2)); + + return data[index0 * size(1) * size(2) + index1 * size(2) + index2]; +} + +template +const T& VarArray3D::operator()(TPIE_OS_SIZE_T index0, TPIE_OS_SIZE_T index1, TPIE_OS_SIZE_T index2) const { + assert(index0 < size(0)); + assert(index1 < size(1)); + assert(index2 < size(2)); + + return data[index0 * size(1) * size(2) + index1 * size(2) + index2]; +} + +template +TPIE_OS_SIZE_T VarArray3D::size() const { + + return dim[0] * dim[1] * dim[2]; +} + +template +TPIE_OS_SIZE_T VarArray3D::size(TPIE_OS_SIZE_T d) const { + assert(d<3); + + return dim[d]; +} + +//---------------------------------------------------------------------- + +#endif diff --git a/fastlib/u/nvasil/tpie/versions.h b/fastlib/u/nvasil/tpie/versions.h new file mode 100644 index 0000000000..ebfd0596eb --- /dev/null +++ b/fastlib/u/nvasil/tpie/versions.h @@ -0,0 +1,21 @@ +// File: versions.h +// Created: 99/11/15 + +// This file defines a macro VERSION that creates a static variable +// __name whose contents contain the given string __id. This is +// intended to be used for creating RCS version identifiers as static +// data in object files and executables. + +// The "compiler_fooler stuff creates a (small) self-referential +// structure that prevents the compiler from warning that __name is +// never referenced. + +// $Id: versions.h,v 1.5 2003/04/17 20:12:01 jan Exp $ + +#ifndef _VERSIONS_H +#define _VERSIONS_H + +// Get definitions for working with Unix and Windows +#include "portability.h" + +#endif // _VERSIONS_H