Skip to content

Commit baa00a9

Browse files
Introduce caching of chunk pointer offsets to reduce linked list traversal
1 parent 5e87349 commit baa00a9

File tree

2 files changed

+43
-20
lines changed

2 files changed

+43
-20
lines changed

android-database-sqlcipher/src/main/cpp/CursorWindow.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,11 @@
1717
#undef LOG_TAG
1818
#define LOG_TAG "CursorWindow"
1919

20-
// #include <utils/Log.h>
21-
// #include <binder/MemoryHeapBase.h>
22-
// #include <binder/MemoryBase.h>
23-
2420
#include <assert.h>
2521
#include <string.h>
2622
#include <stdlib.h>
2723
#include <stdint.h>
28-
2924
#include <jni.h>
30-
// #include <JNIHelp.h>
31-
3225
#include "CursorWindow.h"
3326

3427
namespace sqlcipher {
@@ -73,6 +66,8 @@ void CursorWindow::clear()
7366
mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE;
7467
// Mark the first chunk's next 'pointer' as null
7568
*((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0;
69+
mChunkNumToNextChunkOffset.clear();
70+
mLastChunkPtrOffset = 0;
7671
}
7772

7873
int32_t CursorWindow::freeSpace()
@@ -110,7 +105,7 @@ field_slot_t * CursorWindow::allocRow()
110105
// If the last alloc relocated mData this will be rowSlot's new address, otherwise the value will not change
111106
rowSlot = (row_slot_t*)(mData + rowSlotOffset);
112107

113-
LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset);
108+
LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset);
114109
rowSlot->offset = fieldDirOffset;
115110

116111
return fieldDir;
@@ -150,11 +145,23 @@ uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
150145

151146
row_slot_t * CursorWindow::getRowSlot(int row)
152147
{
153-
LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row);
148+
LOG_WINDOW("getRowSlot entered: requesting row:%d, current row num:%d", row, mHeader->numRows);
149+
unordered_map<int, uint32_t>::iterator result;
154150
int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS;
155151
int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS;
156152
int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
157153
uint8_t * rowChunk = mData + sizeof(window_header_t);
154+
155+
// check for chunkNum in cache
156+
result = mChunkNumToNextChunkOffset.find(chunkNum);
157+
if(result != mChunkNumToNextChunkOffset.end()){
158+
rowChunk = offsetToPtr(result->second);
159+
LOG_WINDOW("Retrieved chunk offset from cache for row:%d", row);
160+
return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
161+
}
162+
163+
// walk the list, this shouldn't occur
164+
LOG_WINDOW("getRowSlot walking list %d times to find rowslot for row:%d", chunkNum, row);
158165
for (int i = 0; i < chunkNum; i++) {
159166
rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset)));
160167
chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
@@ -169,34 +176,45 @@ row_slot_t * CursorWindow::allocRowSlot()
169176
int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS;
170177
int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
171178
uint8_t * rowChunk = mData + sizeof(window_header_t);
172-
LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos);
173-
for (int i = 0; i < chunkNum; i++) {
179+
LOG_WINDOW("allocRowSlot entered: Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d",
180+
mHeader->numRows, chunkNum, chunkPos);
181+
182+
if(mLastChunkPtrOffset != 0){
183+
chunkPtrOffset = mLastChunkPtrOffset;
184+
}
185+
if(chunkNum > 0) {
174186
uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset));
175-
LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
187+
LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
176188
if (nextChunkOffset == 0) {
189+
mLastChunkPtrOffset = chunkPtrOffset;
177190
// Allocate a new row chunk
178191
nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true);
192+
mChunkNumToNextChunkOffset.insert(make_pair(chunkNum, nextChunkOffset));
179193
if (nextChunkOffset == 0) {
180194
return NULL;
181195
}
182196
rowChunk = offsetToPtr(nextChunkOffset);
183-
LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
197+
LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
184198
*((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData;
185199
// Mark the new chunk's next 'pointer' as null
186200
*((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0;
187201
} else {
188-
LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
202+
LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
189203
rowChunk = offsetToPtr(nextChunkOffset);
190204
chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
205+
if(chunkPos == ROW_SLOT_CHUNK_NUM_ROWS - 1){
206+
// prepare to allocate new rowslot_t now at end of row
207+
mLastChunkPtrOffset = chunkPtrOffset;
208+
}
191209
}
192210
}
193211
mHeader->numRows++;
194-
195212
return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
196213
}
197214

198215
field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
199216
{
217+
LOG_WINDOW("getFieldSlotWithCheck entered: row:%d column:%d", row, column);
200218
if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
201219
LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
202220
return NULL;
@@ -216,6 +234,7 @@ field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
216234

217235
uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut)
218236
{
237+
LOG_WINDOW("read_field_slot entered: row:%d, column:%d, slotOut:%p", row, column, slotOut);
219238
if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
220239
LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
221240
return -1;
@@ -229,9 +248,9 @@ uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotO
229248
LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
230249
return -1;
231250
}
232-
LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset);
251+
LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset);
233252
field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset);
234-
LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type);
253+
LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type);
235254

236255
// Copy the data to the out param
237256
slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset;

android-database-sqlcipher/src/main/cpp/CursorWindow.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@
2424
#include <stdint.h>
2525
#include <jni.h>
2626
#include "log.h"
27+
#include <unordered_map>
2728

28-
#define ROW_SLOT_CHUNK_NUM_ROWS 512
29+
#define ROW_SLOT_CHUNK_NUM_ROWS 8
2930
#define INITIAL_WINDOW_SIZE (1024 * 1024)
3031
#define GROW_WINDOW_SIZE_EXTRA INITIAL_WINDOW_SIZE
3132
#define WINDOW_ALLOCATION_UNBOUNDED 0
3233

33-
3434
// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
3535
// with an offset after the rows that points to the next chunk
3636
#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
@@ -47,14 +47,16 @@
4747

4848
#endif
4949

50-
5150
// When defined to true strings are stored as UTF8, otherwise they're UTF16
5251
#define WINDOW_STORAGE_UTF8 0
5352

5453
// When defined to true numberic values are stored inline in the field_slot_t,
5554
// otherwise they're allocated in the window
5655
#define WINDOW_STORAGE_INLINE_NUMERICS 1
5756

57+
using std::make_pair;
58+
using std::tr1::unordered_map;
59+
5860
namespace sqlcipher {
5961

6062
typedef struct
@@ -190,6 +192,8 @@ class CursorWindow
190192
* Offset of the lowest unused data byte in the array.
191193
*/
192194
uint32_t mFreeOffset;
195+
unordered_map<int, uint32_t> mChunkNumToNextChunkOffset;
196+
int mLastChunkPtrOffset;
193197
};
194198

195199
}; // namespace sqlcipher

0 commit comments

Comments
 (0)