Use ashmem for CursorWindows.
Bug: 5332296 The memory dealer introduces additional delays for reclaiming the memory owned by CursorWindows because the Binder object must be finalized. Using ashmem instead gives CursorWindow more direct control over the lifetime of the shared memory region. The provider now allocates the CursorWindows and returns them to clients with a read-only protection bit set on the ashmem region. Improved the encapsulation of CursorWindow. Callers shouldn't need to care about details like how string fields are allocated. Removed the compile-time configuration of string and numeric storage modes to remove some dead weight. Change-Id: I07c2bc2a9c573d7e435dcaecd269d25ea9807acd
This commit is contained in:
parent
3829bc3c35
commit
ec4e0063a1
@ -21,18 +21,8 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <binder/IMemory.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
#define DEFAULT_WINDOW_SIZE 4096
|
||||
#define WINDOW_ALLOCATION_SIZE 4096
|
||||
|
||||
#define ROW_SLOT_CHUNK_NUM_ROWS 16
|
||||
|
||||
// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
|
||||
// with an offset after the rows that points to the next chunk
|
||||
#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#if LOG_NDEBUG
|
||||
|
||||
@ -46,176 +36,157 @@
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// When defined to true strings are stored as UTF8, otherwise they're UTF16
|
||||
#define WINDOW_STORAGE_UTF8 1
|
||||
|
||||
// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window
|
||||
#define WINDOW_STORAGE_INLINE_NUMERICS 1
|
||||
|
||||
namespace android {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t numRows;
|
||||
uint32_t numColumns;
|
||||
} window_header_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t offset;
|
||||
} row_slot_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t type;
|
||||
union {
|
||||
double d;
|
||||
int64_t l;
|
||||
struct {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} buffer;
|
||||
} data;
|
||||
} __attribute__((packed)) field_slot_t;
|
||||
|
||||
#define FIELD_TYPE_NULL 0
|
||||
#define FIELD_TYPE_INTEGER 1
|
||||
#define FIELD_TYPE_FLOAT 2
|
||||
#define FIELD_TYPE_STRING 3
|
||||
#define FIELD_TYPE_BLOB 4
|
||||
|
||||
/**
|
||||
* This class stores a set of rows from a database in a buffer. The begining of the
|
||||
* window has first chunk of row_slot_ts, which are offsets to the row directory, followed by
|
||||
* an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case
|
||||
* window has first chunk of RowSlots, which are offsets to the row directory, followed by
|
||||
* an offset to the next chunk in a linked-list of additional chunk of RowSlots in case
|
||||
* the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
|
||||
* field_slot_t per column, which has the size, offset, and type of the data for that field.
|
||||
* FieldSlot per column, which has the size, offset, and type of the data for that field.
|
||||
* Note that the data types come from sqlite3.h.
|
||||
*
|
||||
* Strings are stored in UTF-8.
|
||||
*/
|
||||
class CursorWindow
|
||||
{
|
||||
class CursorWindow {
|
||||
CursorWindow(const String8& name, int ashmemFd,
|
||||
void* data, size_t size, bool readOnly);
|
||||
|
||||
public:
|
||||
CursorWindow(size_t maxSize);
|
||||
CursorWindow(){}
|
||||
bool setMemory(const sp<IMemory>&);
|
||||
~CursorWindow();
|
||||
/* Field types. */
|
||||
enum {
|
||||
FIELD_TYPE_NULL = 0,
|
||||
FIELD_TYPE_INTEGER = 1,
|
||||
FIELD_TYPE_FLOAT = 2,
|
||||
FIELD_TYPE_STRING = 3,
|
||||
FIELD_TYPE_BLOB = 4,
|
||||
};
|
||||
|
||||
bool initBuffer(bool localOnly);
|
||||
sp<IMemory> getMemory() {return mMemory;}
|
||||
/* Opaque type that describes a field slot. */
|
||||
struct FieldSlot {
|
||||
private:
|
||||
int32_t type;
|
||||
union {
|
||||
double d;
|
||||
int64_t l;
|
||||
struct {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} buffer;
|
||||
} data;
|
||||
|
||||
size_t size() {return mSize;}
|
||||
uint8_t * data() {return mData;}
|
||||
uint32_t getNumRows() {return mHeader->numRows;}
|
||||
uint32_t getNumColumns() {return mHeader->numColumns;}
|
||||
void freeLastRow() {
|
||||
if (mHeader->numRows > 0) {
|
||||
mHeader->numRows--;
|
||||
}
|
||||
}
|
||||
bool setNumColumns(uint32_t numColumns)
|
||||
{
|
||||
uint32_t cur = mHeader->numColumns;
|
||||
if (cur > 0 && cur != numColumns) {
|
||||
LOGE("Trying to go from %d columns to %d", cur, numColumns);
|
||||
return false;
|
||||
}
|
||||
mHeader->numColumns = numColumns;
|
||||
return true;
|
||||
}
|
||||
friend class CursorWindow;
|
||||
} __attribute((packed));
|
||||
|
||||
int32_t freeSpace();
|
||||
~CursorWindow();
|
||||
|
||||
void clear();
|
||||
static status_t create(const String8& name, size_t size, bool localOnly,
|
||||
CursorWindow** outCursorWindow);
|
||||
static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow);
|
||||
|
||||
/**
|
||||
* Allocate a row slot and its directory. The returned
|
||||
* pointer points to the begining of the row's directory
|
||||
* or NULL if there wasn't room. The directory is
|
||||
* initialied with NULL entries for each field.
|
||||
*/
|
||||
field_slot_t * allocRow();
|
||||
status_t writeToParcel(Parcel* parcel);
|
||||
|
||||
/**
|
||||
* Allocate a portion of the window. Returns the offset
|
||||
* of the allocation, or 0 if there isn't enough space.
|
||||
* If aligned is true, the allocation gets 4 byte alignment.
|
||||
*/
|
||||
uint32_t alloc(size_t size, bool aligned = false);
|
||||
inline String8 name() { return mName; }
|
||||
inline size_t size() { return mSize; }
|
||||
inline size_t freeSpace() { return mSize - mHeader->freeOffset; }
|
||||
inline uint32_t getNumRows() { return mHeader->numRows; }
|
||||
inline uint32_t getNumColumns() { return mHeader->numColumns; }
|
||||
|
||||
/**
|
||||
* Copy data into the window at the given offset.
|
||||
*/
|
||||
void copyIn(uint32_t offset, uint8_t const * data, size_t size);
|
||||
void copyIn(uint32_t offset, int64_t data);
|
||||
void copyIn(uint32_t offset, double data);
|
||||
|
||||
void copyOut(uint32_t offset, uint8_t * data, size_t size);
|
||||
int64_t copyOutLong(uint32_t offset);
|
||||
double copyOutDouble(uint32_t offset);
|
||||
|
||||
bool putLong(unsigned int row, unsigned int col, int64_t value);
|
||||
bool putDouble(unsigned int row, unsigned int col, double value);
|
||||
bool putNull(unsigned int row, unsigned int col);
|
||||
|
||||
bool getLong(unsigned int row, unsigned int col, int64_t * valueOut);
|
||||
bool getDouble(unsigned int row, unsigned int col, double * valueOut);
|
||||
bool getNull(unsigned int row, unsigned int col, bool * valueOut);
|
||||
|
||||
uint8_t * offsetToPtr(uint32_t offset) {return mData + offset;}
|
||||
|
||||
row_slot_t * allocRowSlot();
|
||||
|
||||
row_slot_t * getRowSlot(int row);
|
||||
|
||||
/**
|
||||
* return NULL if Failed to find rowSlot or
|
||||
* Invalid rowSlot
|
||||
*/
|
||||
field_slot_t * getFieldSlotWithCheck(int row, int column);
|
||||
field_slot_t * getFieldSlot(int row, int column)
|
||||
{
|
||||
int fieldDirOffset = getRowSlot(row)->offset;
|
||||
return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
|
||||
}
|
||||
|
||||
int64_t getFieldSlotValueLong(field_slot_t* fieldSlot) {
|
||||
#if WINDOW_STORAGE_INLINE_NUMERICS
|
||||
return fieldSlot->data.l;
|
||||
#else
|
||||
return copyOutLong(fieldSlot->data.buffer.offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
double getFieldSlotValueDouble(field_slot_t* fieldSlot) {
|
||||
#if WINDOW_STORAGE_INLINE_NUMERICS
|
||||
return fieldSlot->data.d;
|
||||
#else
|
||||
return copyOutDouble(fieldSlot->data.buffer.offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if WINDOW_STORAGE_UTF8
|
||||
char* getFieldSlotValueString(field_slot_t* fieldSlot) {
|
||||
return reinterpret_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset));
|
||||
}
|
||||
#else
|
||||
char16_t* getFieldSlotValueString(field_slot_t* fieldSlot) {
|
||||
return reinterpret_cast<char16_t*>(offsetToPtr(fieldSlot->data.buffer.offset));
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint8_t * mData;
|
||||
size_t mSize;
|
||||
size_t mMaxSize;
|
||||
window_header_t * mHeader;
|
||||
sp<IMemory> mMemory;
|
||||
status_t clear();
|
||||
status_t setNumColumns(uint32_t numColumns);
|
||||
|
||||
/**
|
||||
* Offset of the lowest unused data byte in the array.
|
||||
* Allocate a row slot and its directory.
|
||||
* The row is initialized will null entries for each field.
|
||||
*/
|
||||
uint32_t mFreeOffset;
|
||||
status_t allocRow();
|
||||
status_t freeLastRow();
|
||||
|
||||
status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size);
|
||||
status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull);
|
||||
status_t putLong(uint32_t row, uint32_t column, int64_t value);
|
||||
status_t putDouble(uint32_t row, uint32_t column, double value);
|
||||
status_t putNull(uint32_t row, uint32_t column);
|
||||
|
||||
/**
|
||||
* Gets the field slot at the specified row and column.
|
||||
* Returns null if the requested row or column is not in the window.
|
||||
*/
|
||||
FieldSlot* getFieldSlot(uint32_t row, uint32_t column);
|
||||
|
||||
inline int32_t getFieldSlotType(FieldSlot* fieldSlot) {
|
||||
return fieldSlot->type;
|
||||
}
|
||||
|
||||
inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) {
|
||||
return fieldSlot->data.l;
|
||||
}
|
||||
|
||||
inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) {
|
||||
return fieldSlot->data.d;
|
||||
}
|
||||
|
||||
inline const char* getFieldSlotValueString(FieldSlot* fieldSlot,
|
||||
size_t* outSizeIncludingNull) {
|
||||
*outSizeIncludingNull = fieldSlot->data.buffer.size;
|
||||
return static_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset));
|
||||
}
|
||||
|
||||
inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) {
|
||||
*outSize = fieldSlot->data.buffer.size;
|
||||
return offsetToPtr(fieldSlot->data.buffer.offset);
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100;
|
||||
|
||||
struct Header {
|
||||
// Offset of the lowest unused byte in the window.
|
||||
uint32_t freeOffset;
|
||||
|
||||
// Offset of the first row slot chunk.
|
||||
uint32_t firstChunkOffset;
|
||||
|
||||
uint32_t numRows;
|
||||
uint32_t numColumns;
|
||||
};
|
||||
|
||||
struct RowSlot {
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct RowSlotChunk {
|
||||
RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS];
|
||||
uint32_t nextChunkOffset;
|
||||
};
|
||||
|
||||
String8 mName;
|
||||
int mAshmemFd;
|
||||
void* mData;
|
||||
size_t mSize;
|
||||
bool mReadOnly;
|
||||
Header* mHeader;
|
||||
|
||||
inline void* offsetToPtr(uint32_t offset) {
|
||||
return static_cast<uint8_t*>(mData) + offset;
|
||||
}
|
||||
|
||||
inline uint32_t offsetFromPtr(void* ptr) {
|
||||
return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a portion of the window. Returns the offset
|
||||
* of the allocation, or 0 if there isn't enough space.
|
||||
* If aligned is true, the allocation gets 4 byte alignment.
|
||||
*/
|
||||
uint32_t alloc(size_t size, bool aligned = false);
|
||||
|
||||
RowSlot* getRowSlot(uint32_t row);
|
||||
RowSlot* allocRowSlot();
|
||||
|
||||
status_t putBlobOrString(uint32_t row, uint32_t column,
|
||||
const void* value, size_t size, int32_t type);
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
@ -19,8 +19,9 @@
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <binder/CursorWindow.h>
|
||||
#include <binder/MemoryHeapBase.h>
|
||||
#include <binder/MemoryBase.h>
|
||||
|
||||
#include <cutils/ashmem.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
@ -28,350 +29,325 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
CursorWindow::CursorWindow(size_t maxSize) :
|
||||
mMaxSize(maxSize)
|
||||
{
|
||||
CursorWindow::CursorWindow(const String8& name, int ashmemFd,
|
||||
void* data, size_t size, bool readOnly) :
|
||||
mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
|
||||
mHeader = static_cast<Header*>(mData);
|
||||
}
|
||||
|
||||
bool CursorWindow::setMemory(const sp<IMemory>& memory)
|
||||
{
|
||||
mMemory = memory;
|
||||
mData = (uint8_t *) memory->pointer();
|
||||
if (mData == NULL) {
|
||||
return false;
|
||||
}
|
||||
mHeader = (window_header_t *) mData;
|
||||
|
||||
// Make the window read-only
|
||||
ssize_t size = memory->size();
|
||||
mSize = size;
|
||||
mMaxSize = size;
|
||||
mFreeOffset = size;
|
||||
LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);
|
||||
return true;
|
||||
CursorWindow::~CursorWindow() {
|
||||
::munmap(mData, mSize);
|
||||
::close(mAshmemFd);
|
||||
}
|
||||
|
||||
bool CursorWindow::initBuffer(bool localOnly)
|
||||
{
|
||||
//TODO Use a non-memory dealer mmap region for localOnly
|
||||
status_t CursorWindow::create(const String8& name, size_t size, bool localOnly,
|
||||
CursorWindow** outCursorWindow) {
|
||||
String8 ashmemName("CursorWindow: ");
|
||||
ashmemName.append(name);
|
||||
ashmemName.append(localOnly ? " (local)" : " (remote)");
|
||||
|
||||
sp<MemoryHeapBase> heap;
|
||||
heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
|
||||
if (heap != NULL) {
|
||||
mMemory = new MemoryBase(heap, 0, mMaxSize);
|
||||
if (mMemory != NULL) {
|
||||
mData = (uint8_t *) mMemory->pointer();
|
||||
if (mData) {
|
||||
mHeader = (window_header_t *) mData;
|
||||
mSize = mMaxSize;
|
||||
|
||||
// Put the window into a clean state
|
||||
clear();
|
||||
LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LOGE("CursorWindow heap allocation failed");
|
||||
return false;
|
||||
status_t result;
|
||||
int ashmemFd = ashmem_create_region(ashmemName.string(), size);
|
||||
if (ashmemFd < 0) {
|
||||
result = -errno;
|
||||
} else {
|
||||
LOGE("failed to create the CursorWindow heap");
|
||||
return false;
|
||||
result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
|
||||
if (result >= 0) {
|
||||
void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
result = -errno;
|
||||
} else {
|
||||
result = ashmem_set_prot_region(ashmemFd, PROT_READ);
|
||||
if (result >= 0) {
|
||||
CursorWindow* window = new CursorWindow(name, ashmemFd,
|
||||
data, size, false /*readOnly*/);
|
||||
result = window->clear();
|
||||
if (!result) {
|
||||
LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
|
||||
"numRows=%d, numColumns=%d, mSize=%d, mData=%p",
|
||||
window->mHeader->freeOffset,
|
||||
window->mHeader->numRows,
|
||||
window->mHeader->numColumns,
|
||||
window->mSize, window->mData);
|
||||
*outCursorWindow = window;
|
||||
return OK;
|
||||
}
|
||||
delete window;
|
||||
}
|
||||
}
|
||||
::munmap(data, size);
|
||||
}
|
||||
::close(ashmemFd);
|
||||
}
|
||||
*outCursorWindow = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
CursorWindow::~CursorWindow()
|
||||
{
|
||||
// Everything that matters is a smart pointer
|
||||
status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
|
||||
String8 name = parcel->readString8();
|
||||
|
||||
status_t result;
|
||||
int ashmemFd = parcel->readFileDescriptor();
|
||||
if (ashmemFd == int(BAD_TYPE)) {
|
||||
result = BAD_TYPE;
|
||||
} else {
|
||||
ssize_t size = ashmem_get_size_region(ashmemFd);
|
||||
if (size < 0) {
|
||||
result = UNKNOWN_ERROR;
|
||||
} else {
|
||||
int dupAshmemFd = ::dup(ashmemFd);
|
||||
if (dupAshmemFd < 0) {
|
||||
result = -errno;
|
||||
} else {
|
||||
void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
result = -errno;
|
||||
} else {
|
||||
CursorWindow* window = new CursorWindow(name, dupAshmemFd,
|
||||
data, size, true /*readOnly*/);
|
||||
LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
|
||||
"numRows=%d, numColumns=%d, mSize=%d, mData=%p",
|
||||
window->mHeader->freeOffset,
|
||||
window->mHeader->numRows,
|
||||
window->mHeader->numColumns,
|
||||
window->mSize, window->mData);
|
||||
*outCursorWindow = window;
|
||||
return OK;
|
||||
}
|
||||
::close(dupAshmemFd);
|
||||
}
|
||||
}
|
||||
}
|
||||
*outCursorWindow = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
void CursorWindow::clear()
|
||||
{
|
||||
status_t CursorWindow::writeToParcel(Parcel* parcel) {
|
||||
status_t status = parcel->writeString8(mName);
|
||||
if (!status) {
|
||||
status = parcel->writeDupFileDescriptor(mAshmemFd);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t CursorWindow::clear() {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
|
||||
mHeader->firstChunkOffset = sizeof(Header);
|
||||
mHeader->numRows = 0;
|
||||
mHeader->numColumns = 0;
|
||||
mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE;
|
||||
// Mark the first chunk's next 'pointer' as null
|
||||
*((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0;
|
||||
|
||||
RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
|
||||
firstChunk->nextChunkOffset = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int32_t CursorWindow::freeSpace()
|
||||
{
|
||||
int32_t freeSpace = mSize - mFreeOffset;
|
||||
if (freeSpace < 0) {
|
||||
freeSpace = 0;
|
||||
status_t CursorWindow::setNumColumns(uint32_t numColumns) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
return freeSpace;
|
||||
|
||||
uint32_t cur = mHeader->numColumns;
|
||||
if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
|
||||
LOGE("Trying to go from %d columns to %d", cur, numColumns);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
mHeader->numColumns = numColumns;
|
||||
return OK;
|
||||
}
|
||||
|
||||
field_slot_t * CursorWindow::allocRow()
|
||||
{
|
||||
status_t CursorWindow::allocRow() {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// Fill in the row slot
|
||||
row_slot_t * rowSlot = allocRowSlot();
|
||||
RowSlot* rowSlot = allocRowSlot();
|
||||
if (rowSlot == NULL) {
|
||||
return NULL;
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
// Allocate the slots for the field directory
|
||||
size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t);
|
||||
uint32_t fieldDirOffset = alloc(fieldDirSize);
|
||||
size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
|
||||
uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
|
||||
if (!fieldDirOffset) {
|
||||
mHeader->numRows--;
|
||||
LOG_WINDOW("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows);
|
||||
return NULL;
|
||||
LOG_WINDOW("The row failed, so back out the new row accounting "
|
||||
"from allocRowSlot %d", mHeader->numRows);
|
||||
return NO_MEMORY;
|
||||
}
|
||||
field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset);
|
||||
memset(fieldDir, 0x0, fieldDirSize);
|
||||
FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
|
||||
memset(fieldDir, 0, fieldDirSize);
|
||||
|
||||
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);
|
||||
LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n",
|
||||
mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
|
||||
rowSlot->offset = fieldDirOffset;
|
||||
|
||||
return fieldDir;
|
||||
return OK;
|
||||
}
|
||||
|
||||
uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
|
||||
{
|
||||
int32_t size;
|
||||
status_t CursorWindow::freeLastRow() {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
if (mHeader->numRows > 0) {
|
||||
mHeader->numRows--;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
uint32_t CursorWindow::alloc(size_t size, bool aligned) {
|
||||
uint32_t padding;
|
||||
if (aligned) {
|
||||
// 4 byte alignment
|
||||
padding = 4 - (mFreeOffset & 0x3);
|
||||
padding = (~mHeader->freeOffset + 1) & 3;
|
||||
} else {
|
||||
padding = 0;
|
||||
}
|
||||
|
||||
size = requestedSize + padding;
|
||||
|
||||
if (size > freeSpace()) {
|
||||
LOGV("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size,
|
||||
freeSpace(), mHeader->numRows);
|
||||
// Only grow the window if the first row doesn't fit
|
||||
if (mHeader->numRows > 1) {
|
||||
LOGV("not growing since there are already %d row(s), max size %d", mHeader->numRows,
|
||||
mMaxSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find a new size that will fit the allocation
|
||||
int allocated = mSize - freeSpace();
|
||||
int newSize = mSize + WINDOW_ALLOCATION_SIZE;
|
||||
while (size > (newSize - allocated)) {
|
||||
newSize += WINDOW_ALLOCATION_SIZE;
|
||||
if (newSize > mMaxSize) {
|
||||
LOGE("Attempting to grow window beyond max size (%d)", mMaxSize);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
LOG_WINDOW("found size %d", newSize);
|
||||
mSize = newSize;
|
||||
uint32_t offset = mHeader->freeOffset + padding;
|
||||
uint32_t nextFreeOffset = offset + size;
|
||||
if (nextFreeOffset > mSize) {
|
||||
LOGE("Window is full: requested allocation %d bytes, "
|
||||
"free space %d bytes, window size %d bytes",
|
||||
size, freeSpace(), mSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t offset = mFreeOffset + padding;
|
||||
mFreeOffset += size;
|
||||
mHeader->freeOffset = nextFreeOffset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
row_slot_t * CursorWindow::getRowSlot(int row)
|
||||
{
|
||||
LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row);
|
||||
int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
|
||||
uint8_t * rowChunk = mData + sizeof(window_header_t);
|
||||
for (int i = 0; i < chunkNum; i++) {
|
||||
rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset)));
|
||||
chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
|
||||
CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
|
||||
uint32_t chunkPos = row;
|
||||
RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
|
||||
offsetToPtr(mHeader->firstChunkOffset));
|
||||
while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
|
||||
chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
|
||||
chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
}
|
||||
return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
|
||||
LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row);
|
||||
return &chunk->slots[chunkPos];
|
||||
}
|
||||
|
||||
row_slot_t * CursorWindow::allocRowSlot()
|
||||
{
|
||||
int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
|
||||
uint8_t * rowChunk = mData + sizeof(window_header_t);
|
||||
LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos);
|
||||
for (int i = 0; i < chunkNum; i++) {
|
||||
uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset));
|
||||
LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
|
||||
if (nextChunkOffset == 0) {
|
||||
// Allocate a new row chunk
|
||||
nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true);
|
||||
if (nextChunkOffset == 0) {
|
||||
CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
|
||||
uint32_t chunkPos = mHeader->numRows;
|
||||
RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
|
||||
offsetToPtr(mHeader->firstChunkOffset));
|
||||
while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
|
||||
chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
|
||||
chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
}
|
||||
if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
|
||||
if (!chunk->nextChunkOffset) {
|
||||
chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
|
||||
if (!chunk->nextChunkOffset) {
|
||||
return NULL;
|
||||
}
|
||||
rowChunk = offsetToPtr(nextChunkOffset);
|
||||
LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
|
||||
*((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData;
|
||||
// Mark the new chunk's next 'pointer' as null
|
||||
*((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0;
|
||||
} else {
|
||||
LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
|
||||
rowChunk = offsetToPtr(nextChunkOffset);
|
||||
chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
|
||||
}
|
||||
chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
|
||||
chunk->nextChunkOffset = 0;
|
||||
chunkPos = 0;
|
||||
}
|
||||
mHeader->numRows++;
|
||||
|
||||
return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
|
||||
mHeader->numRows += 1;
|
||||
return &chunk->slots[chunkPos];
|
||||
}
|
||||
|
||||
field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
|
||||
{
|
||||
if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
|
||||
LOGE("Failed to read row# %d, column# from a CursorWindow which has %d rows, %d columns.",
|
||||
row, column, mHeader->numRows, mHeader->numColumns);
|
||||
return NULL;
|
||||
}
|
||||
row_slot_t * rowSlot = getRowSlot(row);
|
||||
if (!rowSlot) {
|
||||
LOGE("Failed to find rowSlot for row %d", row);
|
||||
return NULL;
|
||||
}
|
||||
if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
|
||||
LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
|
||||
return NULL;
|
||||
}
|
||||
int fieldDirOffset = rowSlot->offset;
|
||||
return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
|
||||
CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
|
||||
if (row >= mHeader->numRows || column >= mHeader->numColumns) {
|
||||
LOGE("Failed to read row %d, column %d from a CursorWindow which "
|
||||
"has %d rows, %d columns.",
|
||||
row, column, mHeader->numRows, mHeader->numColumns);
|
||||
return NULL;
|
||||
}
|
||||
RowSlot* rowSlot = getRowSlot(row);
|
||||
if (!rowSlot) {
|
||||
LOGE("Failed to find rowSlot for row %d.", row);
|
||||
return NULL;
|
||||
}
|
||||
FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
|
||||
return &fieldDir[column];
|
||||
}
|
||||
|
||||
void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size)
|
||||
{
|
||||
assert(offset + size <= mSize);
|
||||
memcpy(mData + offset, data, size);
|
||||
status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
|
||||
return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
|
||||
}
|
||||
|
||||
void CursorWindow::copyIn(uint32_t offset, int64_t data)
|
||||
{
|
||||
assert(offset + sizeof(int64_t) <= mSize);
|
||||
memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t));
|
||||
status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
|
||||
size_t sizeIncludingNull) {
|
||||
return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
|
||||
}
|
||||
|
||||
void CursorWindow::copyIn(uint32_t offset, double data)
|
||||
{
|
||||
assert(offset + sizeof(double) <= mSize);
|
||||
memcpy(mData + offset, (uint8_t *)&data, sizeof(double));
|
||||
}
|
||||
status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
|
||||
const void* value, size_t size, int32_t type) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size)
|
||||
{
|
||||
assert(offset + size <= mSize);
|
||||
memcpy(data, mData + offset, size);
|
||||
}
|
||||
|
||||
int64_t CursorWindow::copyOutLong(uint32_t offset)
|
||||
{
|
||||
int64_t value;
|
||||
assert(offset + sizeof(int64_t) <= mSize);
|
||||
memcpy(&value, mData + offset, sizeof(int64_t));
|
||||
return value;
|
||||
}
|
||||
|
||||
double CursorWindow::copyOutDouble(uint32_t offset)
|
||||
{
|
||||
double value;
|
||||
assert(offset + sizeof(double) <= mSize);
|
||||
memcpy(&value, mData + offset, sizeof(double));
|
||||
return value;
|
||||
}
|
||||
|
||||
bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
||||
if (!fieldSlot) {
|
||||
return false;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
#if WINDOW_STORAGE_INLINE_NUMERICS
|
||||
fieldSlot->data.l = value;
|
||||
#else
|
||||
int offset = alloc(sizeof(int64_t));
|
||||
uint32_t offset = alloc(size);
|
||||
if (!offset) {
|
||||
return false;
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
copyIn(offset, value);
|
||||
memcpy(offsetToPtr(offset), value, size);
|
||||
|
||||
fieldSlot->type = type;
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = sizeof(int64_t);
|
||||
#endif
|
||||
fieldSlot->data.buffer.size = size;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
||||
if (!fieldSlot) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
fieldSlot->type = FIELD_TYPE_INTEGER;
|
||||
return true;
|
||||
fieldSlot->data.l = value;
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
||||
if (!fieldSlot) {
|
||||
return false;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
#if WINDOW_STORAGE_INLINE_NUMERICS
|
||||
fieldSlot->data.d = value;
|
||||
#else
|
||||
int offset = alloc(sizeof(int64_t));
|
||||
if (!offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
copyIn(offset, value);
|
||||
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = sizeof(double);
|
||||
#endif
|
||||
fieldSlot->type = FIELD_TYPE_FLOAT;
|
||||
return true;
|
||||
fieldSlot->data.d = value;
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool CursorWindow::putNull(unsigned int row, unsigned int col)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
||||
if (!fieldSlot) {
|
||||
return false;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
fieldSlot->type = FIELD_TYPE_NULL;
|
||||
fieldSlot->data.buffer.offset = 0;
|
||||
fieldSlot->data.buffer.size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*valueOut = getFieldSlotValueLong(fieldSlot);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*valueOut = getFieldSlotValueDouble(fieldSlot);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fieldSlot->type != FIELD_TYPE_NULL) {
|
||||
*valueOut = false;
|
||||
} else {
|
||||
*valueOut = true;
|
||||
}
|
||||
return true;
|
||||
return OK;
|
||||
}
|
||||
|
||||
}; // namespace android
|
||||
|
@ -752,7 +752,7 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob)
|
||||
|
||||
int result = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
|
||||
if (result < 0) {
|
||||
status = -result;
|
||||
status = result;
|
||||
} else {
|
||||
void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
@ -760,7 +760,7 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob)
|
||||
} else {
|
||||
result = ashmem_set_prot_region(fd, PROT_READ);
|
||||
if (result < 0) {
|
||||
status = -result;
|
||||
status = result;
|
||||
} else {
|
||||
status = writeInt32(1);
|
||||
if (!status) {
|
||||
|
Loading…
Reference in New Issue
Block a user