private FindRecordResult _findRecord(IComparable<RecordKey> keytest, bool equal_is_after)
{
// read state
byte[] search_buffer = new byte[FIND_RECORD_BUFSIZE];
int read_result_size, read_size;
int restart_count = 0;
FindRecordResult result = default(FindRecordResult);
// cursor state
int startpos = 0;
int endpos = (int)this.datastream.Length;
int midpoint_pos = 0xbeefed; // distinct but uninitalized
// start by decoding the first record in the block
int eol_marker_pos = -1;
mpss state = mpss.HAVE_START;
switch (state) {
case mpss.BISECT:
if (endpos == startpos) {
return result;
}
{ // find out if we need to account for an odd-size split
int midpoint_pos_delta = endpos - startpos;
int midpoint_offset = 0xbeefed;
if ((midpoint_pos_delta & 1) == 1) {
midpoint_offset = (midpoint_pos_delta - 1) / 2;
} else {
midpoint_offset = midpoint_pos_delta / 2;
}
midpoint_pos = startpos + midpoint_offset;
}
if (midpoint_pos > endpos) {
midpoint_pos = startpos;
}
goto case mpss.FIND_REC_FORWARD;
case mpss.FIND_REC_FORWARD:
restart_count = 0;
goto case mpss.FIND_REC_FORWARD_RESTART; // fall through
case mpss.FIND_REC_FORWARD_RESTART:
restart_count++;
// scan forward until we find an END_OF_LINE record boundary
{
int cur_stream_pos = midpoint_pos;
do {
int bufpos = 0;
this.datastream.Seek(cur_stream_pos, SeekOrigin.Begin);
read_size = (int)Math.Min(search_buffer.Length,endpos-cur_stream_pos);
read_result_size = this.datastream.Read(search_buffer,0,read_size);
while (bufpos < read_result_size) {
if (search_buffer[bufpos] == SegmentBlockBasicEncoder.END_OF_LINE) {
eol_marker_pos = cur_stream_pos + bufpos;
goto case mpss.HAVE_START;
}
bufpos++;
}
cur_stream_pos += read_result_size;
} while (cur_stream_pos < endpos);
}
if (restart_count > 1) {
throw new Exception("INTERNAL ERROR : SegmentBlockBasic._findRecord, restarted more than once!");
}
// ran out of datastream and didn't find END_OF_LINE!
// move to the beginning of the buffer and restart,
midpoint_pos = startpos;
// but don't rescan that stuff we just saw between midpoint_pos and endpos
endpos = midpoint_pos+1;
goto case mpss.FIND_REC_FORWARD_RESTART;
// fyi - we considered bisecting the first half, but if the records are so big relative
// to our midpoint that there were none in the second half, it's because our records
// are really large relative to the range we are scanning, so it's safer just to
// start at the BEGINNING of the whole range.
case mpss.HAVE_START:
// we have a record_start_stream_pos, so decode the record and test against the comparison
{
RecordLocator rloc;
int record_start_pos = eol_marker_pos + 1;
if (record_start_pos == endpos) {
// we found the end of the last record in the midpoint-region,
// however, we're not sure where it starts, let's
// decode the record at the start of the region instead.
record_start_pos = startpos;
}
// decode the record
this.datastream.Seek(record_start_pos, SeekOrigin.Begin);
rloc.record = SegmentBlockBasicDecoder._decodeRecordFromBlock(this.datastream);
rloc.start_pos = record_start_pos;
rloc.after_end_pos = (int)this.datastream.Position;
rloc.have_record = true;
int compare_result = keytest.CompareTo(rloc.record.Key);
if (compare_result == 0) {
if (equal_is_after) {
compare_result = -1; // rec==keytest should be after
} else {
compare_result = 1; // rec==keytest should be before
}
}
if (compare_result < 0) { // record is AFTER keytest
result.after_keytest = rloc;
// we know this record is after us, so scan only before it
endpos = rloc.start_pos;
} else if (compare_result > 0) { // record is BEFORE keytest
result.before_keytest = rloc;
// we know this record is before us, so scan only after it
startpos = rloc.after_end_pos;
}
}
goto case mpss.BISECT;
default:
throw new Exception("unknown mpss state: " + state.ToString());
} // end switch(mpss state)
}