#include "LinkedList.h" // Note, currently this is an #include in "DS_cube.h", but in future "DS_cube.h" should be absolete and deleted. #define CACHING_IS_ENABLED 1 // Set to '0' to disable file cache code (faster overall) #define CHECK_DATA_RANGE 0 // For each 'datac' request, does boundary checking (set break point) #define CACHING_USE_LINKED_LIST (0 && CACHING_IS_ENABLED) // Keeps most recently accessed spots at head of list, // rather than always bumping to end as new stuff comes in. // ========================================================================= // // ========================================================================= // // // TEMPLATE FOR ANY type of DS_cube // // ========================================================================= // // ========================================================================= // // If file cache is used, this keeps track of total files that have been made // to provide a unique name for each one static int dst_cache_file_num = 0; // Options for DSt cube object operations typedef int dst_options; #define DST_DO_NOT_APPEND_SIZE 0x0001 #define DST_UPDATE_FILE_NAME 0x0002 // Private options #define DST_FILE_IS_RGB 0x0010 typedef unsigned int mem_block; // Smaller block size allows larger DS cubes, but slightly slower performance #define DST_SMALL_BLOCK 16000000 // Current limit appears to be around 1.76 GB cube with this size #define DST_MEDIUM_BLOCK 64000000 #define DST_SINGLE_BLOCK 2000000000 // Only one block (limited to less than 1 GB) #define DST_AUTO_SIZE_BLOCK 0 // Will use 1 block for cubes smaller than 512 MB, DST_SMALL_BLOCK for anything larger typedef enum dst_status { // Error codes for DSt_cube DST_NO_ERROR = 0, DST_FILE_NAME_TOO_LONG, DST_FILE_IO_ERROR, DST_ERROR_MEMORY_ALLOCATION, } dst_status; // ------------------------------------------------------------------------------------------------------------ // // Cache type descriptions // // CACHE_DISABLED: Regular operation with DST_AUTO_SIZE_BLOCK. // // CACHE_FULL_SLICE: Operates at slice size scale (wX * wY). This means that things will be read in or written // out at one slice chunks as cache is missed. If cache is missed rarely, this is fairly // efficient. Best used if cube is traveled in a linear style (BS distance transform). // // CACHE_FULL_ROW: Operates at row size scale (wX). Allows many more small cache segments, with each one being // read in fairly quickly. Good if cube is used in random ways (such as growing a sphere or // SS distance transform), but not as efficient as FULL_SLCIE for linear travel over. // // CACHE_SUB_ROW: Operates at sub row segments that are power of 2 size. Most overhead, but better than // full row type for large samples (1000+ dimension). Additional parameter of // 'sub_row_size_2pow' can be provided to specify segment size, which will be: // segment_size = 2^sub_row_size_2pow; // // ------------------------------------------------------------------------------------------------------------ // typedef enum cache_type { CACHE_DISABLED = 0, CACHE_FULL_SLICE, // Fastest but uses most memory, reads in a full slice at a time CACHE_FULL_ROW, // Uses less memory, reads in full rows at a time. More overhead. CACHE_SUB_ROW, // Uses least memory, most overhead } cache_type; #define INFINITE_MEMORY_64BIT 0x0FFFFFFFFFFFFFFF template <class T> class DSt_cube { // PUBLIC functions public: // Constructor for non-disc cached cube. DSt_cube( unsigned short nwZ, unsigned short nwY, unsigned short nwX, mem_block max_block_size_bytes = DST_AUTO_SIZE_BLOCK ); // Constructor for disc cached cube (must use 'datac()' functions for this). DSt_cube( unsigned short nwZ, unsigned short nwY, unsigned short nwX, cache_type cache_config, UINT64 desired_cache_size_bytes = 134217728, unsigned char sub_row_size_2pow = 7 ); ~DSt_cube( void ); // Common use functions void clear(); void setall(T val); void floodWithNewValue(unsigned short z, unsigned short y, unsigned short x, T &new_val); void floodWithNewValue(Vect3usi &coord, T &new_val); // Saving/Reading functions dst_status writeToFile(char* output_prefix, dst_options options=0); dst_status writeToFileRGB(char* output_prefix, T maxval=0, dst_options options=0, CProgressCtrl *progress_bar=NULL); dst_status readFromFile(char *input_name, unsigned short start_at_files_z_index=0); // Public cache operations void forceCacheSync(); void resizeCacheBuffer( UINT64 new_cache_size_bytes ); // ------ INLINED functions ------ // // Cache getter/setter functions. If cache disabled, use ->data[#z][#y][#x] for fastest access. T datac(unsigned short z, unsigned short y, unsigned short x); void datac(unsigned short z, unsigned short y, unsigned short x, T new_val); T datac(Vect3usi &coord); void datac(Vect3usi &coord, T value); // PUBLIC data unsigned short wZ; unsigned short wY; unsigned short wX; Vect3usi dimensions; // Same as wZ, wY, wX, but in single structure form T ***data; // This is a 3d array representing each spot in cube T **rows; // This is memory for each slice pointer private: // PRIVATE functions dst_status allocateCube(unsigned short nwZ, unsigned short nwY, unsigned short nwX); HANDLE OpenOutputFile(char *output_prefix, dst_options options=0); // Cache functions dst_status allocateCacheMemory(UINT64 desired_size ); dst_status allocateBufferMemory( UINT64 desired_size ); T dataCache( unsigned short z, unsigned short y, unsigned short x ); void dataCache( unsigned short z, unsigned short y, unsigned short x, T new_val ); void invalidateOldestInValidityTable( ); void obtainFreeCache( ); void readIntoCache( unsigned short z, unsigned short y, unsigned short x ); void writeBufferToEntireCacheFile(T *buffer, unsigned int bytes_in_buffer); // PRIVATE data T **memory_block_ptrs; // This is memory for actual data (broken up into multiple blocks) // -------------------------------------------------------- // // If memory is broken into multiple blocks, these are used // mem_block max_block_size; unsigned int memory_num_of_blocks; unsigned int memory_total_used; unsigned short slices_per_block; unsigned short slices_per_block_last; // Last block might have different number of slices than rest // -------------------------------------------------- // // If disc cache is used, these are support variables // typedef struct cache_entry_info { unsigned short z, y, x; // This is the cached slice/row/subrow location T **data; // Pointer to the actual data segment bool needs_sync; // Whether or not this row is out of sync with disc cache contents #if CACHING_USE_LINKED_LIST // For linked list, also need a pointer back to the linked list node LinkedListNode<cache_entry_info> *queue_node_ptr; #endif } cache_entry_info; // General cache info char cache_file_name[50]; HANDLE cache_file; cache_type cache_state; bool cache_is_full; int cache_sub_row_shift_val; unsigned short cache_entry_size; Vect3usi cache_entry_dimensions; unsigned short cache_sub_row_last_entry_size; unsigned short cache_sub_row_last_seg_indx; // Data for keeping track of cache cache_entry_info ****cache_validity_table; // Tells which rows are currently in cache and points to their info for quick access cache_entry_info ***cache_validity_table_rows; // Row info for above table cache_entry_info **cache_validity_table_mem; // Memory for the above table Vect3usi cache_validity_table_dim; // Dimensions of the validity table int cache_number_of_entries, cache_available_indx; #if CACHING_USE_LINKED_LIST LinkedList<cache_entry_info> *cache_age_queue; // If full, segment that was used longest ago gets thrown LinkedListNode<cache_entry_info> *oldest_seg_ptr; // This is used as a temp pointer in many places #else cache_entry_info *cache_age_list; // Circular array of cache rows, if cache full, oldest row gets thrown int cache_oldest_indx; #endif }; // =================================================================================== // // =================================================================================== // // INLINED functions // // =================================================================================== // // =================================================================================== // template <class T> inline void DSt_cube<T>::floodWithNewValue(Vect3usi &coord, T &new_val) { this->floodWithNewValue( coord.z, coord.y, coord.x, new_val ); } // These are wrappers for cache access. If cache disabled, this should result // in faster normal access for Vectored coordinates. template <class T> inline T DSt_cube<T>::datac( unsigned short z, unsigned short y, unsigned short x ) { #if CHECK_DATA_RANGE if ( (z >= wZ) || (y >= wY) || (x >= wX) ) { // Out of bounds, log and exit write_to_log("DSt_cube<T>::datac - Out of bounds coordiniate requested (z%d,y%d,x%d)", z, y, x); exit(1); } #endif #if CACHING_IS_ENABLED return ( (cache_state) ? (this->dataCache( z, y, x )) : (data[z][y][x]) ); #else return data[z][y][x]; #endif }; template <class T> inline void DSt_cube<T>::datac( unsigned short z, unsigned short y, unsigned short x, T new_value ) { #if CHECK_DATA_RANGE if ( (z >= wZ) || (y >= wY) || (x >= wX) ) { // Out of bounds, log and exit write_to_log("DSt_cube<T>::datac - Out of bounds coordiniate requested (z%d,y%d,x%d)", z, y, x); exit(1); } #endif #if CACHING_IS_ENABLED ( (cache_state) ? (this->dataCache( z, y, x, new_value )) : (data[z][y][x] = new_value) ); #else data[z][y][x] = new_value; #endif }; // The following are just short cuts for Vector objects, and really call above template <class T> inline T DSt_cube<T>::datac( Vect3usi &coord ) { return ( this->datac(coord.z, coord.y, coord.x) ); }; template <class T> inline void DSt_cube<T>::datac( Vect3usi &coord, T value ) { this->datac(coord.z, coord.y, coord.x, value); }; // =================================================================================== // // Other template files belonging to this object // // =================================================================================== // #include "Dst_cube.hpp" #include "DSt_cube_Operations.hpp" #include "Dst_cube_Cache.hpp" #include "Dst_cube_FileIO.hpp"