Revision 64 (by gdshaw@RISCPKG.ORG, 2007/05/21 05:44:33) Disabled exceptions and enabled optimisation to reduce code size.
// This file is part of the free Filer module for RISC OS.
// Copyright � 2007 Graham Shaw.
// Redistribution and modification are permitted under the terms of the
// GNU General Public License (version 2 or any later version).

#include <cstring>
#include <new>

#include "oslib/osfscontrol.h"

#include "sort_method.h"
#include "sort_by_name.h"
#include "directory.h"

/** The attribute bit used to indicate whether or not a file is selected.
 * See the note about storage of the selection flag in the declaration of
 * directory::value_type.
static const int attr_selected=0x80000000;


directory::value_type::value_type(osgbpb_info* info):

bool directory::value_type::selected() const
	return (_info->attr&attr_selected)!=0;

void directory::value_type::selected(bool state)
	if (state) _info->attr|=attr_selected;
	else _info->attr&=~attr_selected;

void directory::reserve(size_type capacity)
	// Do nothing unless the required capacity exceeds the current capacity.
	if (capacity>_capacity)
		// Allocate new, larger array.
		value_type* new_values=new(std::nothrow) value_type[capacity];
		value_type* old_values=_values;

		// Copy values from old array to new array.
		for (size_type i=0;i!=_size;++i)

		// Switch to new array.

		// Deallocate old array.
		delete[] old_values;

directory::iterator directory::lower_bound(value_type info)
	// Initially, result could lie anywhere from 0 to _size inclusive.
	size_type first=0;
	size_type last=_size;

	// Repeat until result range has narrowed to one possibility.
	while (last!=first)
		// Go to index in middle of range (rounding down: first<=mid<last).
		size_type mid=(first+last)/2;
		if (_sort->less(*info,*_values[mid]))
			// If required index is lower than the index probed then
			// the insertion point can be no later than (but may be
			// equal to) the index probed.
			// Otherwise, the insertion point must be later than the
			// index probed.

	// Return the result as an iterator.
	// (Note that first and last would be equally valid here.)
	return _values+first;

void directory::insert(value_type info,iterator pos)
	// Ensure that there is sufficient space for this insertion.
	// (The iterator is temporarily converted to an index during this
	// operation in order to survive a possible relocation of the data.)
	size_type index=pos-begin();
	if (_capacity==_size) reserve(_size+1);

	// Make space for new entry.
	for (iterator i=end();i!=pos;--i)

	// Insert new entry.

void directory::erase(iterator pos)
	// Move following entries into space vacated by deletion.
	for (iterator i=pos,i_end=end();i!=i_end;++i)

directory::directory(sort_method& sort):

	for (size_type i=0;i!=_size;++i)
		osgbpb_info* info=_values[i];
		delete[] (int*)info;
	delete[] _values;

void directory::sort(sort_method& sort)
	// Change the sorting method.

	// Perform an insertion sort.
	for (unsigned int i=1;i<_size;++i)
		value_type value=_values[i];
		unsigned int j=i;
		while (j&&_sort->less(*value,*_values[j-1]))

void directory::load(const char* pathname,int* buffer,size_t buffer_size)
	// Record current sorting method then change to sort-by-name.
	// (The sorting method must not take account of anything but
	// the name during a reload, otherwise the existing selection
	// may not be correctly preserved.)
	sort_method* user_sort=_sort;
	static sort_by_name by_name;

	// Mark all existing entries as deleted (but do not actually delete
	// them, so that they can still be found and their selection state
	// copied).
	for (size_type i=0,i_end=size();i!=i_end;++i)

	// Iterate through directory.
	int context=0;
	while (context!=-1)
		// Read a buffer-full of directory entries.
		int count=0;
		if (xosgbpb_dir_entries_info(pathname,(osgbpb_info_list*)buffer,

		// Reserve space for new entries now (as opposed to doing so
		// piecemeal) to reduce the number of reallocations.

		// Process buffer.
		const int* p=buffer;
		for (int i=0;i!=count;++i)
			// Calculate length of current entry (in words).
			osgbpb_info* info_src=(osgbpb_info*)p;
			size_t info_words=5+((strlen(info_src->name)+4)>>2);

			// Choose insertion point.
			iterator f=lower_bound(info_src);
			if (f!=end()&&!_sort->less(*info_src,**f)&&
				// If an entry with that leafname is already present
				// then re-use that entry.
				int selected=((*f)->attr&attr_selected);
				// If the leafname is not already present then create
				// a new entry and insert it into the directory.
				value_type info(
					(osgbpb_info*)new(std::nothrow) int[info_words]);

			// Move to next entry.

	// Iterate through directory, removing any entries which have been
	// marked as deleted.  Some care is needed here because it is not
	// safe to simply proceed from index 0 to _size: the act of deleting
	// an entry changes the size of the array and the index must be
	// adjusted accordingly.
	size_t i=0;
	while (i!=_size)
		if (_values[i]->obj_type==fileswitch_NOT_FOUND)
			// Make a note of the entry to be deleted.
			osgbpb_info* info=_values[i];

			// Remove the entry from the directory.

			// Deallocate the entry.
			delete[] (int*)info;
			// If nothing was deleted then increment the index.
			// (Conversely, and quite importantly, if something /was/
			// deleted then do /not/ increment the index.)

	// Revert to the sorting method that had previously been chosen.