static RC VdbeSorterInitMerge(Context ctx, VdbeCursor cursor, ref long bytes) { VdbeSorter sorter = cursor, Sorter; long bytes2 = 0; // Total bytes in all opened PMAs RC rc = RC.OK; // Initialize the iterators. int i; // Used to iterator through aIter[] for (i = 0; i < SORTER_MAX_MERGE_COUNT; i++) { VdbeSorterIter iter = sorter.Iters[i]; rc = VdbeSorterIterInit(ctx, sorter, sorter.ReadOffset, iter, ref bytes2); sorter.ReadOffset = iter.Eof; Debug.Assert(rc != RC.OK || sorter.ReadOffset <= sorter.WriteOffset); if (rc != RC.OK || sorter.ReadOffset >= sorter.WriteOffset) { break; } } // Initialize the aTree[] array. for (i = sorter.Trees.length - 1; rc == RC.OK && i > 0; i--) { rc = VdbeSorterDoCompare(cursor, i); } bytes = bytes2; return(rc); }
static RC VdbeSorterIterVarint(Context ctx, VdbeSorterIter p, out ulong out_) { int bufferIdx = (int)(p.ReadOffset % p.Buffer.length); if (bufferIdx != 0 && (p.Buffer.length - bufferIdx) >= 9) { p.ReadOffset += ConvertEx.GetVarint(p.Buffer[bufferIdx], out out_); } else { byte[] varint = new byte[16]; byte[] a; int i = 0; do { RC rc = VdbeSorterIterRead(ctx, p, 1, ref a); if (rc != 0) { return(rc); } varint[(i++) & 0xf] = a[0]; } while ((a[0] & 0x80) != 0); ConvertEx.GetVarint(varint, out out_); } return(RC.OK); }
static object VdbeSorterRowkey(VdbeSorter sorter, out int keyLength) { if (sorter.Trees.data != null) { VdbeSorterIter iter = sorter.Iters[sorter.Trees.data[1]]; keyLength = iter.Key.length; return(iter.Key); } keyLength = sorter.Record.N; return(sorter.Record.P); }
static RC VdbeSorterIterInit(Context ctx, VdbeSorter sorter, long start, VdbeSorterIter iter, ref long bytes) { Debug.Assert(sorter.WriteOffset > start); Debug.Assert(iter.Alloc.data == null); Debug.Assert(iter.Buffer.data == null); int bufferLength = ctx.DBs[0].Bt.GetPageSize(); iter.File = sorter.Temp1; iter.ReadOffset = start; iter.Alloc.length = 128; iter.Alloc.data = (byte[])C._tagalloc(ctx, iter.Alloc.length); iter.Buffer.length = bufferLength; iter.Buffer.data = (byte[])C._tagalloc(ctx, bufferLength); RC rc = RC.OK; if (iter.Buffer.data == null) { rc = RC.NOMEM; } else { int bufferIdx = start % bufferLength; if (bufferIdx != 0) { int read = bufferLength - bufferIdx; if ((start + read) > sorter.WriteOffset) { read = (int)(sorter.WriteOffset - start); } rc = sorter.Temp1.Read(iter.Buffer[bufferIdx], read, start); Debug.Assert(rc != RC.IOERR_SHORT_READ); } if (rc == RC.OK) { iter.Eof = sorter.WriteOffset; ulong bytes2; // Size of PMA in bytes rc = VdbeSorterIterVarint(ctx, iter, ref bytes2); iter.Eof = iter.ReadOffset + bytes2; bytes += bytes2; } } if (rc == RC.OK) { rc = VdbeSorterIterNext(ctx, iter); } return(rc); }
static RC VdbeSorterIterNext(Context ctx, VdbeSorterIter iter) { if (iter.ReadOffset >= iter.Eof) { VdbeSorterIterZero(ctx, iter); // This is an EOF condition return(RC.OK); } ulong recordSize; // Size of record in bytes RC rc = VdbeSorterIterVarint(ctx, iter, ref recordSize); if (rc == RC.OK) { iter.Key.length = (int)recordSize; rc = VdbeSorterIterRead(ctx, iter, (int)recordSize, ref iter.Key.data); } return(rc); }
static RC VdbeSorterDoCompare(VdbeCursor cursor, int idx) { VdbeSorter sorter = cursor.Sorter; Debug.Assert(idx < sorter.Trees.length && idx > 0); int i1; int i2; if (idx >= (sorter.Trees.length / 2)) { i1 = (idx - sorter.Trees.length / 2) * 2; i2 = i1 + 1; } else { i1 = sorter.Trees[idx * 2]; i2 = sorter.Trees[idx * 2 + 1]; } VdbeSorterIter p1 = sorter.Iters[i1]; VdbeSorterIter p2 = sorter.Iters[i2]; int i; if (p1.File == null) { i = i2; } else if (p2.File == null) { i = i1; } else { Debug.Assert(sorter.Unpacked != null); // allocated in vdbeSorterMerge() int r; VdbeSorterCompare(cursor, 0, p1.Key, p1.Key.length, p2.Key, p2.Key.length, ref r); i = (r <= 0 ? i1 : i2); } sorter.Trees[idx] = i; return(RC.OK); }
static RC VdbeSorterIterRead(Context ctx, VdbeSorterIter p, int bytes, ref byte[] out_) { Debug.Assert(p.Buffer.data != null); // If there is no more data to be read from the buffer, read the next p->nBuffer bytes of data from the file into it. Or, if there are less // than p->nBuffer bytes remaining in the PMA, read all remaining data. int bufferIdx = (int)(p.ReadOffset % p.Buffer.length); // Offset within buffer to read from if (bufferIdx == 0) { // Determine how many bytes of data to read. int read = (int)((p.Eof - p.ReadOffset) > p.Buffer.length ? p.Buffer.length : p.Eof - p.ReadOffset); // Bytes to read from disk Debug.Assert(read > 0); // Read data from the file. Return early if an error occurs. RC rc = p.File.Read(p.Buffer, read, p.ReadOffset); Debug.Assert(rc != RC.IOERR_SHORT_READ); if (rc != RC.OK) { return(rc); } } int avail = p.Buffer.length - bufferIdx; // Bytes of data available in buffer if (bytes <= avail) { // The requested data is available in the in-memory buffer. In this case there is no need to make a copy of the data, just return a // pointer into the buffer to the caller. out_ = p.Buffer[bufferIdx]; p.ReadOffset += bytes; } // The requested data is not all available in the in-memory buffer. In this case, allocate space at p->aAlloc[] to copy the requested // range into. Then return a copy of pointer p->aAlloc to the caller. else { // Extend the p->aAlloc[] allocation if required. if (p.Alloc.length < bytes) { int newSize = p.Alloc.length * 2; while (bytes > newSize) { newSize = newSize * 2; } p.Alloc.data = (byte[])C._tagrealloc(ctx, 0, p.Alloc.data, newSize); if (p.Alloc.data == null) { return(RC.NOMEM); } p.Alloc.length = newSize; } // Copy as much data as is available in the buffer into the start of p->aAlloc[]. C._memcpy(p.Alloc.data, p.Buffer[bufferIdx], avail); p.ReadOffset += avail; // The following loop copies up to p->nBuffer bytes per iteration into the p->aAlloc[] buffer. int remaining = bytes - avail; // Bytes remaining to copy while (remaining > 0) { int copy = remaining; // Number of bytes to copy if (remaining > p.Buffer.length) { copy = p.Buffer.length; } byte[] next; // Pointer to buffer to copy data from RC rc = VdbeSorterIterRead(ctx, p, copy, ref next); if (rc != RC.OK) { return(rc); } Debug.Assert(next != p.Alloc); C._memcpy(p.Alloc[bytes - remaining], next, copy); remaining -= copy; } out_ = p.Alloc; } return(RC.OK); }
public RC SorterRewind(Context ctx, VdbeCursor cursor, ref bool eof) { VdbeSorter sorter = cursor.Sorter; Debug.Assert(sorter != null); // If no data has been written to disk, then do not do so now. Instead, sort the VdbeSorter.pRecord list. The vdbe layer will read data directly // from the in-memory list. if (sorter.PMAs == 0) { eof = (sorter.Record == null); Debug.Assert(sorter.Trees.data = null); return(VdbeSorterSort(cursor)); } // Write the current in-memory list to a PMA. RC rc = VdbeSorterListToPMA(ctx, cursor); if (rc != RC.OK) { return(rc); } // Allocate space for aIter[] and aTree[]. int iters = sorter.PMAs; // Number of iterators used if (iters > SORTER_MAX_MERGE_COUNT) { iters = SORTER_MAX_MERGE_COUNT; } Debug.Assert(iters > 0); int n = 2; while (n < iters) { n += n; // Power of 2 >= iters } int bytes = n * (sizeof(int) + sizeof(VdbeSorterIter)); // Bytes of space required for aIter/aTree sorter.Iters = (VdbeSorterIter)C._tagalloc(ctx, bytes); if (sorter.Iters == null) { return(RC.NOMEM); } sorter.Trees = sorter.Iters[n]; sorter.Trees.length = n; int newIdx; // Index of new, merged, PMA VFile temp2 = null; // Second temp file to use long write2 = 0; // Write offset for pTemp2 do { for (newIdx = 0; rc == RC.OK && newIdx * SORTER_MAX_MERGE_COUNT < sorter.PMAs; newIdx++) { FileWriter writer; writer._memset(); // Object used to write to disk // If there are SORTER_MAX_MERGE_COUNT or less PMAs in file pTemp1, initialize an iterator for each of them and break out of the loop. // These iterators will be incrementally merged as the VDBE layer calls sqlite3VdbeSorterNext(). // // Otherwise, if pTemp1 contains more than SORTER_MAX_MERGE_COUNT PMAs, initialize interators for SORTER_MAX_MERGE_COUNT of them. These PMAs // are merged into a single PMA that is written to file pTemp2. long writes; // Number of bytes in new PMA rc = VdbeSorterInitMerge(ctx, cursor, ref writes); Debug.Assert(rc != RC.OK || sorter.Iters[sorter.Trees[1]].File); if (rc != RC.OK || sorter.PMAs <= SORTER_MAX_MERGE_COUNT) { break; } // Open the second temp file, if it is not already open. if (temp2 == null) { Debug.Assert(write2 == 0); rc = VdbeSorterOpenTempFile(ctx, ref temp2); } if (rc == RC.OK) { bool eof = false; FileWriterInit(ctx, temp2, ref writer, write2); FileWriterWriteVarint(writer, writes); while (rc == RC.OK && !eof) { VdbeSorterIter iter = sorter.Iters[sorter.Trees[1]]; Debug.Assert(iter.File); FileWriterWriteVarint(writer, iter.Key.length); FileWriterWrite(writer, iter.Key, iter.Key.length); rc = SorterNext(ctx, cursor, eof); } RC rc2 = FileWriterFinish(ctx, writer, write2); if (rc == RC.OK) { rc = rc2; } } } if (sorter.PMAs <= SORTER_MAX_MERGE_COUNT) { break; } else { VFile tmp = sorter.Temp1; sorter.PMAs = newIdx; sorter.Temp1 = temp2; temp2 = tmp; sorter.WriteOffset = write2; sorter.ReadOffset = 0; write2 = 0; } } while (rc == RC.OK); if (temp2) { temp2.CloseAndFree(); } eof = (sorter.Iters[sorter.Trees[1]].File == null); return(rc); }
public const int SORTER_MAX_MERGE_COUNT = 16; // Maximum number of segments to merge in a single pass. #endregion #region Sorter Iter static void VdbeSorterIterZero(Context ctx, VdbeSorterIter iter) { C._tagfree(ctx, ref iter.Alloc.data); C._tagfree(ctx, ref iter.Buffer.data); iter._memset(); }
static RC VdbeSorterIterRead(Context ctx, VdbeSorterIter p, int bytes, ref byte[] out_) { Debug.Assert(p.Buffer.data != null); // If there is no more data to be read from the buffer, read the next p->nBuffer bytes of data from the file into it. Or, if there are less // than p->nBuffer bytes remaining in the PMA, read all remaining data. int bufferIdx = (int)(p.ReadOffset % p.Buffer.length); // Offset within buffer to read from if (bufferIdx == 0) { // Determine how many bytes of data to read. int read = (int)((p.Eof - p.ReadOffset) > p.Buffer.length ? p.Buffer.length : p.Eof - p.ReadOffset); // Bytes to read from disk Debug.Assert(read > 0); // Read data from the file. Return early if an error occurs. RC rc = p.File.Read(p.Buffer, read, p.ReadOffset); Debug.Assert(rc != RC.IOERR_SHORT_READ); if (rc != RC.OK) return rc; } int avail = p.Buffer.length - bufferIdx; // Bytes of data available in buffer if (bytes <= avail) { // The requested data is available in the in-memory buffer. In this case there is no need to make a copy of the data, just return a // pointer into the buffer to the caller. out_ = p.Buffer[bufferIdx]; p.ReadOffset += bytes; } // The requested data is not all available in the in-memory buffer. In this case, allocate space at p->aAlloc[] to copy the requested // range into. Then return a copy of pointer p->aAlloc to the caller. else { // Extend the p->aAlloc[] allocation if required. if (p.Alloc.length < bytes) { int newSize = p.Alloc.length * 2; while (bytes > newSize) newSize = newSize * 2; p.Alloc.data = (byte[])C._tagrealloc(ctx, 0, p.Alloc.data, newSize); if (p.Alloc.data == null) return RC.NOMEM; p.Alloc.length = newSize; } // Copy as much data as is available in the buffer into the start of p->aAlloc[]. C._memcpy(p.Alloc.data, p.Buffer[bufferIdx], avail); p.ReadOffset += avail; // The following loop copies up to p->nBuffer bytes per iteration into the p->aAlloc[] buffer. int remaining = bytes - avail; // Bytes remaining to copy while (remaining > 0) { int copy = remaining; // Number of bytes to copy if (remaining > p.Buffer.length) copy = p.Buffer.length; byte[] next; // Pointer to buffer to copy data from RC rc = VdbeSorterIterRead(ctx, p, copy, ref next); if (rc != RC.OK) return rc; Debug.Assert(next != p.Alloc); C._memcpy(p.Alloc[bytes - remaining], next, copy); remaining -= copy; } out_ = p.Alloc; } return RC.OK; }
static RC VdbeSorterIterInit(Context ctx, VdbeSorter sorter, long start, VdbeSorterIter iter, ref long bytes) { Debug.Assert(sorter.WriteOffset > start); Debug.Assert(iter.Alloc.data == null); Debug.Assert(iter.Buffer.data == null); int bufferLength = ctx.DBs[0].Bt.GetPageSize(); iter.File = sorter.Temp1; iter.ReadOffset = start; iter.Alloc.length = 128; iter.Alloc.data = (byte[])C._tagalloc(ctx, iter.Alloc.length); iter.Buffer.length = bufferLength; iter.Buffer.data = (byte[])C._tagalloc(ctx, bufferLength); RC rc = RC.OK; if (iter.Buffer.data == null) rc = RC.NOMEM; else { int bufferIdx = start % bufferLength; if (bufferIdx != 0) { int read = bufferLength - bufferIdx; if ((start + read) > sorter.WriteOffset) read = (int)(sorter.WriteOffset - start); rc = sorter.Temp1.Read(iter.Buffer[bufferIdx], read, start); Debug.Assert(rc != RC.IOERR_SHORT_READ); } if (rc == RC.OK) { iter.Eof = sorter.WriteOffset; ulong bytes2; // Size of PMA in bytes rc = VdbeSorterIterVarint(ctx, iter, ref bytes2); iter.Eof = iter.ReadOffset + bytes2; bytes += bytes2; } } if (rc == RC.OK) rc = VdbeSorterIterNext(ctx, iter); return rc; }
static RC VdbeSorterIterNext(Context ctx, VdbeSorterIter iter) { if (iter.ReadOffset >= iter.Eof) { VdbeSorterIterZero(ctx, iter); // This is an EOF condition return RC.OK; } ulong recordSize; // Size of record in bytes RC rc = VdbeSorterIterVarint(ctx, iter, ref recordSize); if (rc == RC.OK) { iter.Key.length = (int)recordSize; rc = VdbeSorterIterRead(ctx, iter, (int)recordSize, ref iter.Key.data); } return rc; }
static RC VdbeSorterIterVarint(Context ctx, VdbeSorterIter p, out ulong out_) { int bufferIdx = (int)(p.ReadOffset % p.Buffer.length); if (bufferIdx != 0 && (p.Buffer.length - bufferIdx) >= 9) p.ReadOffset += ConvertEx.GetVarint(p.Buffer[bufferIdx], out out_); else { byte[] varint = new byte[16]; byte[] a; int i = 0; do { RC rc = VdbeSorterIterRead(ctx, p, 1, ref a); if (rc != 0) return rc; varint[(i++) & 0xf] = a[0]; } while ((a[0] & 0x80) != 0); ConvertEx.GetVarint(varint, out out_); } return RC.OK; }