#ifndef VARRAY_H
#define VARRAY_H
// This is a template for a virtual array of type T.
#include <stdio.h>
#include <dos.h>
#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif
template <class T>
class varray
{
private:
int expanded_memory_allocated(void);
// Allocated expanded memory. If this can't be done,
// an attempt will be made to use a disk instead.
unsigned int expanded_memory_handle;
int free_expanded_memory;
int free_disk_memory;
// The temporary file used for virtual memory needs to be
// freed.
void free_memory(void);
// Free real memory used for pages, etc.
int free_real_memory;
// Real memory needs to be freed.
int map_expanded_memory(int physical_page,long logical_page);
// Associate a physical page in the physical page frame
// with a logical page of expanded memory.
int memory_allocated(void);
// Allocate real memory for pages, etc.
long num_elements_in_array;
// Number of elements in the array.
unsigned int num_elements_per_page;
// Number of array elements in a page.
int num_real_pages;
// Number of pages kept in real memory.
char *page_frame_ptr;
// Pointer to expanded memory physical frame.
T **real_page;
// A page in real memory.
long *starting_element_num;
// Index of first array element in page in real memory.
FILE *vm;
// Temporary file used for virtual memory.
long *vm_access;
// "When" a page in real memory was last accessed.
long vm_access_num;
// Current "time".
public:
int allocated(void) {return (free_real_memory &&
(free_expanded_memory || free_disk_memory));}
varray(T& initialized_element,long element_count,
int real_page_count=4,unsigned int page_size=32768);
// Construct a virtual array of type T. The arguments are as follow:
//
// initialized_element -- an initialized element of type T.
//
// element_count -- the number of elements in the array.
//
// real_page_count -- the number of pages to be kept in memory.
// This should be at least as large as the maximum number of elements
// of the array referenced at once. Small values result in too much
// paging; large values result in too much overhead searching for the
// page containing an element.
//
// page_size -- the number of bytes in a page. This must be at least
// sizeof(T). This is used only when a disk is used for virtual memory.
// (When expanded memory is used, the page size is effectively 32K.) 32K
// is probably optimal due to the overhead of searching for a page.
~varray(void);
// If expanded memory was used, free it. If a temporary file was used,
// close (and so delete) it. If real memory was allocated, free it.
T *vm_ptr(long element_num);
// Return a pointer to an element of the array. When T is a structure, it
// is faster to use this pointer than to overload [] and search virtual memory
// for each component of the structure.
T& operator[](long element_num) {return *vm_ptr(element_num);}
// Return an element of the array by overloading [].
};
template <class T>
varray<T>::varray(T& initialized_element,long element_count,int real_page_count,
unsigned int page_size)
// Construct a virtual array of type T. The arguments are as follow:
//
// initialized_element -- an initialized element of type T.
//
// element_count -- the number of elements in the array.
//
// real_page_count -- the number of pages to be kept in memory.
// This should be at least as large as the maximum number of elements
// of the array referenced at once. Small values result in too much
// paging; large values result in too much overhead searching for the
// page containing an element.
//
// page_size -- the number of bytes in a page. This must be at least
// sizeof(T). This is used only when a disk is used for virtual memory.
// (When expanded memory is used, the page size is effectively 32K.) 32K
// is probably optimal due to the overhead of searching for a page.
{
unsigned int element_index;
long element_num;
free_real_memory=FALSE;
free_disk_memory=FALSE;
free_expanded_memory=FALSE;
num_real_pages=real_page_count;
num_elements_in_array=element_count;
if (sizeof(T) <= 32768)
{
num_elements_per_page=32768/sizeof(T);
free_expanded_memory=expanded_memory_allocated();
}
if (free_expanded_memory)
if (memory_allocated()) // Allocate space for the active pages.
{
free_real_memory=TRUE;
long expanded_memory_page_num=0L;
int real_page_num=0;
vm_access_num=0;
int successful=TRUE;
for (element_num=(long) 0;
((successful) && (element_num < num_elements_in_array));
element_num+=((long) num_elements_per_page))
{
if (real_page_num < real_page_count)
// Initialize real page.
{
vm_access_num++;
for (element_index=(unsigned int) 0;
element_index < num_elements_per_page;
element_index++)
real_page[real_page_num][element_index]
=initialized_element;
vm_access[real_page_num]=vm_access_num;
starting_element_num[real_page_num]=element_num;
real_page_num++;
}
// Initialize virtual page.
if (successful=map_expanded_memory(0,expanded_memory_page_num++))
{
if (successful
=map_expanded_memory(1,expanded_memory_page_num++))
memcpy((void *) page_frame_ptr,(void *) &(real_page[0][0]),
num_elements_per_page*sizeof(T));
}
}
if (! successful)
cerr
<< "Fatal error: there is an unexpected problem with expanded "
<< "memory." << '\n';
}
else
cerr << "Fatal error: not enough real memory for virtual array."
<< '\n';
else
{
num_elements_per_page=page_size/sizeof(T);
if ((vm=tmpfile()) == NULL) // Get a file for the virtual memory.
cerr << "Fatal error: cannot open virtual memory." << '\n';
else
if (memory_allocated()) // Allocate space for the active pages.
{
free_real_memory=TRUE;
free_disk_memory=TRUE;
int real_page_num=0;
vm_access_num=0;
int successful=TRUE;
for (element_num=(long) 0;
((successful) && (element_num < num_elements_in_array));
element_num+=((long) num_elements_per_page))
{
if (real_page_num < real_page_count)
// Initialize real page.
{
vm_access_num++;
for (element_index=(unsigned int) 0;
element_index < num_elements_per_page;
element_index++)
real_page[real_page_num][element_index]
=initialized_element;
vm_access[real_page_num]=vm_access_num;
starting_element_num[real_page_num]=element_num;
real_page_num++;
}
// Initialize virtual page.
successful=(fwrite(&(real_page[0][0]),
sizeof(T),num_elements_per_page,vm)
== num_elements_per_page);
}
if (! successful)
cerr
<< "Fatal error: not enough disk space for virtual array."
<< '\n';
}
else
cerr << "Fatal error: not enough real memory for virtual array."
<< '\n';
}
}
template <class T>
varray<T>::~varray()
// If expanded memory was used, free it. If a temporary file was used,
// close (and so delete) it. If real memory was allocated, free it.
{
if (free_expanded_memory)
{
union REGS input_reg;
union REGS output_reg;
input_reg.h.ah=0x45;
input_reg.x.dx=expanded_memory_handle;
int86(0x67,&input_reg,&output_reg);
}
if (free_disk_memory)
fclose(vm);
if (free_real_memory)
free_memory();
};
template <class T>
int varray<T>::expanded_memory_allocated()
{
char *EMM_device_name = "EMMXXXX0";
union REGS input_reg;
union
{
unsigned long device_name_address;
char *device_name_ptr;
};
union REGS output_reg;
union
{
unsigned long pf_address;
char *pf_ptr;
};
long pages_needed;
int result;
struct SREGS segment_reg;
typedef T *t_ptr;
result=FALSE;
pages_needed=num_elements_in_array/long(num_elements_per_page);
if (pages_needed*long(num_elements_per_page) < num_elements_in_array)
pages_needed++;
pages_needed+=pages_needed; // VARRAY uses 32K byte pages;
// expanded memory uses 16K byte pages.
if (pages_needed <= 0xffff)
{
input_reg.h.ah=0x35;
input_reg.h.al=0x67;
intdosx(&input_reg,&output_reg,&segment_reg);
device_name_address=(((unsigned long) (segment_reg.es)) << 16) + 10L;
if (memcmp(EMM_device_name,device_name_ptr,8) == 0)
{
input_reg.h.ah=0x42;
int86(0x67,&input_reg,&output_reg);
if ((output_reg.h.ah == 0)
&& (pages_needed <= long(output_reg.x.bx)))
{
input_reg.h.ah=0x43;
input_reg.x.bx=(unsigned int) pages_needed;
int86(0x67,&input_reg,&output_reg);
expanded_memory_handle=output_reg.x.dx;
if (output_reg.h.ah == 0)
{
input_reg.h.ah=0x41;
int86(0x67,&input_reg,&output_reg);
if (result=(output_reg.h.ah == 0))
{
pf_address=(((unsigned long) (output_reg.x.bx)) << 16);
page_frame_ptr=pf_ptr;
}
}
}
}
}
return result;
}
template <class T>
int varray<T>::memory_allocated()
// Allocate real memory for pages, etc.
{
int result;
int real_page_num;
typedef T *t_ptr;
if (result=((vm_access=new long[num_real_pages]) != NULL))
{
if (result=((starting_element_num=new long[num_real_pages]) != NULL))
if (result=((real_page=new t_ptr[num_real_pages]) != NULL))
{
for (real_page_num=0;
((result) && (real_page_num < num_real_pages));
real_page_num++)
result=((real_page[real_page_num]
=new T[num_elements_per_page]) != NULL);
if (! result)
{
--real_page_num;
while (real_page_num > 0)
delete[] real_page[--real_page_num];
delete[] real_page;
}
}
else
{
delete[] starting_element_num;
delete[] vm_access;
}
else
delete[] vm_access;
}
return(result);
};
template <class T>
void varray<T>::free_memory()
// Free real memory used for pages, etc.
{
for (int real_page_num=0; real_page_num < num_real_pages; real_page_num++)
delete[] real_page[real_page_num];
delete[] real_page;
delete[] starting_element_num;
delete[] vm_access;
return;
};
template <class T>
T *varray<T>::vm_ptr(
long element_num)
// Return a pointer to an element of the array.
{
long earliest_access;
long expanded_memory_page_num;
int num_oldest_real_page;
int page_found;
int real_page_num;
T *result;
long virtual_page_num;
vm_access_num++;
page_found=FALSE;
num_oldest_real_page=0;
earliest_access=vm_access[0];
for (real_page_num=0;
((! page_found) && (real_page_num < num_real_pages));
real_page_num++)
if ((element_num >= starting_element_num[real_page_num])
&& (element_num < ((long) num_elements_per_page)
+(starting_element_num[real_page_num])))
page_found=TRUE;
else
{
if (vm_access[real_page_num] < earliest_access)
{
earliest_access=vm_access[real_page_num];
num_oldest_real_page=real_page_num;
}
}
if (page_found) // Page is already in real memory.
{
real_page_num--;
result=&(real_page[real_page_num]
[element_num-starting_element_num[real_page_num]]);
vm_access[real_page_num]=vm_access_num;
}
else // Swap oldest page in real memory for the one sought.
if (free_expanded_memory)
{
virtual_page_num=starting_element_num[num_oldest_real_page]
/((long) num_elements_per_page);
expanded_memory_page_num=virtual_page_num;
expanded_memory_page_num+=expanded_memory_page_num;
map_expanded_memory(0,expanded_memory_page_num++);
map_expanded_memory(1,expanded_memory_page_num);
memcpy((void *) page_frame_ptr,
(void *) &(real_page[num_oldest_real_page][0]),
num_elements_per_page*sizeof(T));
virtual_page_num=element_num/((long) num_elements_per_page);
expanded_memory_page_num=virtual_page_num;
expanded_memory_page_num+=expanded_memory_page_num;
map_expanded_memory(0,expanded_memory_page_num++);
map_expanded_memory(1,expanded_memory_page_num);
memcpy((void *) &(real_page[num_oldest_real_page][0]),
(void *) page_frame_ptr,
num_elements_per_page*sizeof(T));
starting_element_num[num_oldest_real_page]
=virtual_page_num*((long) num_elements_per_page);
result=&(real_page[num_oldest_real_page][
element_num-starting_element_num[num_oldest_real_page]]);
vm_access[num_oldest_real_page]=vm_access_num;
}
else
{
fseek(vm,
starting_element_num[num_oldest_real_page]*((long) sizeof(T)),
SEEK_SET);
fwrite(&(real_page[num_oldest_real_page][0]),sizeof(T),
num_elements_per_page,vm);
starting_element_num[num_oldest_real_page]
=element_num/((long) num_elements_per_page);
(starting_element_num[num_oldest_real_page])
*=((long) num_elements_per_page);
fseek(vm,
starting_element_num[num_oldest_real_page]*((long) sizeof(T)),
SEEK_SET);
fread(&(real_page[num_oldest_real_page][0]),sizeof(T),
num_elements_per_page,vm);
result=&(real_page[num_oldest_real_page][
element_num-starting_element_num[num_oldest_real_page]]);
vm_access[num_oldest_real_page]=vm_access_num;
}
return result;
};
template <class T>
int varray<T>::map_expanded_memory(
int physical_page,
long logical_page)
// Associate a physical page in the physical page frame with a logical page
// of expanded memory.
{
union REGS input_reg;
union REGS output_reg;
input_reg.h.ah=0x44;
input_reg.h.al=physical_page;
input_reg.x.bx=(unsigned int) logical_page;
input_reg.x.dx=expanded_memory_handle;
int86(0x67,&input_reg,&output_reg);
return (output_reg.h.ah == 0);
}
#endif