/// SinkWorld split buffer class
/// Copyright 2001 Neil Hodgson
/// All implementations use the intrinsic char type which is 1 byte 
/// in C++ and 2 bytes in Java and C#.

class cbx {
private:
    
char *body;
    
int size;
    
int length;
    
int part1Length;
    
int gapLength;
    
int growSize;

    
/// Move the gap to a particular position so that insertion and 
    
/// deletion at that point will not require much copying and 
    
/// hence be fast.
    
void GapTo(int position) {
        
if (position != part1Length) {
            
if (position < part1Length) {
                
int diff = part1Length - position;
                
for (int i = 0; i < diff; i++)
                    
body[part1Length + gapLength - i - 1] = 
                        
body[part1Length - i - 1];
            
} else {    // position > part1Length
                
int diff = position - part1Length;
                
for (int i = 0; i < diff; i++)
                    
body[part1Length + i] = 
                        
body[part1Length + gapLength + i];
            
}
            
part1Length = position;
        
}
    
}

    
/// Reallocate the storage for the buffer to be newSize and 
    
/// copy exisiting contents to the new buffer.
    
/// Must not be used to decrease the size of the buffer.
    
void ReAllocate(int newSize) {
        
// Move the gap to the end
        
GapTo(length);
        
char *newBody = new char[newSize];
        
if (size != 0) {
            
for (int i = 0; i < size; i++)
                
newBody[i] = body[i];
            
delete []body;
        
}
        
body = newBody;
        
gapLength += newSize - size;
        
size = newSize;
    
}

    
/// Check that there is room in the buffer for an insertion, 
    
/// reallocating if more space needed.
    
void RoomFor(int insertionLength) {
        
if (gapLength <= insertionLength) {
            
if (growSize * 6 < size)
                
growSize *= 2;
            
ReAllocate(size + insertionLength + growSize);
        
}
    
}

public:
    
/// Construct a split buffer.
    
cbx(int initialLength, int growSize_) {
        
growSize = growSize_;
        
size = 0;
        
length = 0;
        
part1Length = 0;
        
gapLength = 0;
        
ReAllocate(initialLength);
    
}

    
~cbx() {
        
delete []body;
        
body = NULL;
    
}

    
/// Retrieve the character at a particular position.
    
/// Retrieving positions outside the range of the buffer works 
    
/// and returns 0.
    
char CharAt(int position) {
        
if (position < part1Length) {
            
if (position < 0) {
                
return '\0';
            
} else {
                
return body[position];
            
}
        
} else {
            
if (position >= length) {
                
return '\0';
            
} else {
                
return body[gapLength + position];
            
}
        
}
    
}

    
/// Retrieve the length of the buffer.
    
int Length() {
        
return length;
    
}

    
/// Retrieve a range of text from the buffer.
    
/// Retrieving positions outside the range of the buffer fails.
    
void RetrieveString(int position, char s[], int retrieveLength) {
        
int i = 0;
        
int pos = position;
        
while ((i < retrieveLength) && (pos < part1Length)) {
            
s[i++] = body[pos++];
        
}
        
while (i < retrieveLength) {
            
s[i++] = body[gapLength + pos++];
        
}
    
}

    
/// Insert text into the buffer.
    
/// Inserting at positions outside the current range throws a
    
/// BadPositionException.
    
void InsertString(int position, char s[], int insertLength) {
        
if (insertLength > 0) {
            
if ((position < 0) || (position > length)) {
                
throw new BadPositionException();
            
}
            
RoomFor(insertLength);
            
GapTo(position);
            
for (int i = 0; i < insertLength; i++)
                
body[part1Length + i] = s[i];
            
length += insertLength;
            
part1Length += insertLength;
            
gapLength -= insertLength;
        
}
    
}

    
/// Fill an already allocated range with a value.
    
/// Modifying outside the current range throws a
    
/// BadPositionException.
    
void FillRange(int position, char ch, int fillLength) {
        
if (fillLength > 0) {
            
if ((position < 0) || ((position + fillLength) > length)) {
                
throw new BadPositionException();
            
}
            
int i = 0;
            
int pos = position;
            
while ((i < fillLength) && (pos < part1Length)) {
                
body[pos++] = ch;
                
i++;
            
}
            
while (i < fillLength) {
                
body[gapLength + pos++] = ch;
                
i++;
            
}
        
}
    
}

    
/// Insert a number of zero characters into the buffer.
    
/// Inserting at positions outside the current range fails.
    
void InsertZeros(int position, int insertLength) {
        
if (insertLength > 0) {
            
RoomFor(insertLength);
            
GapTo(position);
            
for (int i = 0; i < insertLength; i++)
                
body[part1Length + i] = '\0';
            
length += insertLength;
            
part1Length += insertLength;
            
gapLength -= insertLength;
        
}
    
}

    
/// Delete a range from the buffer.
    
/// Deleting positions outside the current range fails.
    
void DeleteChars(int position, int deleteLength) {
        
if (deleteLength > 0) {
            
GapTo(position);
            
length -= deleteLength;
            
gapLength += deleteLength;
        
}
    
}
};