|  | /* | 
|  | * Copyright (C) 2006-2007 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #undef LOG_TAG | 
|  | #define LOG_TAG "CursorWindow" | 
|  |  | 
|  | #include <utils/Log.h> | 
|  | #include <binder/CursorWindow.h> | 
|  | #include <binder/MemoryHeapBase.h> | 
|  | #include <binder/MemoryBase.h> | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <string.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | CursorWindow::CursorWindow(size_t maxSize) : | 
|  | mMaxSize(maxSize) | 
|  | { | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | bool CursorWindow::initBuffer(bool localOnly) | 
|  | { | 
|  | //TODO Use a non-memory dealer mmap region for localOnly | 
|  |  | 
|  | 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; | 
|  | } else { | 
|  | LOGE("failed to create the CursorWindow heap"); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | CursorWindow::~CursorWindow() | 
|  | { | 
|  | // Everything that matters is a smart pointer | 
|  | } | 
|  |  | 
|  | void CursorWindow::clear() | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | int32_t CursorWindow::freeSpace() | 
|  | { | 
|  | int32_t freeSpace = mSize - mFreeOffset; | 
|  | if (freeSpace < 0) { | 
|  | freeSpace = 0; | 
|  | } | 
|  | return freeSpace; | 
|  | } | 
|  |  | 
|  | field_slot_t * CursorWindow::allocRow() | 
|  | { | 
|  | // Fill in the row slot | 
|  | row_slot_t * rowSlot = allocRowSlot(); | 
|  | if (rowSlot == NULL) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | // Allocate the slots for the field directory | 
|  | size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t); | 
|  | uint32_t fieldDirOffset = alloc(fieldDirSize); | 
|  | if (!fieldDirOffset) { | 
|  | mHeader->numRows--; | 
|  | LOG_WINDOW("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows); | 
|  | return NULL; | 
|  | } | 
|  | field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset); | 
|  | memset(fieldDir, 0x0, 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); | 
|  | rowSlot->offset = fieldDirOffset; | 
|  |  | 
|  | return fieldDir; | 
|  | } | 
|  |  | 
|  | uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned) | 
|  | { | 
|  | int32_t size; | 
|  | uint32_t padding; | 
|  | if (aligned) { | 
|  | // 4 byte alignment | 
|  | padding = 4 - (mFreeOffset & 0x3); | 
|  | } 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 = mFreeOffset + padding; | 
|  | mFreeOffset += size; | 
|  | 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)); | 
|  | } | 
|  | return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); | 
|  | LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row); | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | 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)); | 
|  | } | 
|  | } | 
|  | mHeader->numRows++; | 
|  |  | 
|  | return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut) | 
|  | { | 
|  | if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) { | 
|  | LOGE("Can't read row# %d, col# %d from CursorWindow. Make sure your Cursor is initialized correctly.", | 
|  | row, column); | 
|  | return -1; | 
|  | } | 
|  | row_slot_t * rowSlot = getRowSlot(row); | 
|  | if (!rowSlot) { | 
|  | LOGE("Failed to find rowSlot for row %d", row); | 
|  | return -1; | 
|  | } | 
|  | if (rowSlot->offset == 0 || rowSlot->offset >= mSize) { | 
|  | LOGE("Invalid rowSlot, offset = %d", rowSlot->offset); | 
|  | return -1; | 
|  | } | 
|  | LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset); | 
|  | field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset); | 
|  | 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); | 
|  |  | 
|  | // Copy the data to the out param | 
|  | slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset; | 
|  | slotOut->data.buffer.size = fieldDir[column].data.buffer.size; | 
|  | slotOut->type = fieldDir[column].type; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size) | 
|  | { | 
|  | assert(offset + size <= mSize); | 
|  | memcpy(mData + offset, data, size); | 
|  | } | 
|  |  | 
|  | void CursorWindow::copyIn(uint32_t offset, int64_t data) | 
|  | { | 
|  | assert(offset + sizeof(int64_t) <= mSize); | 
|  | memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t)); | 
|  | } | 
|  |  | 
|  | void CursorWindow::copyIn(uint32_t offset, double data) | 
|  | { | 
|  | assert(offset + sizeof(double) <= mSize); | 
|  | memcpy(mData + offset, (uint8_t *)&data, sizeof(double)); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | if (!fieldSlot) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #if WINDOW_STORAGE_INLINE_NUMERICS | 
|  | fieldSlot->data.l = 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(int64_t); | 
|  | #endif | 
|  | fieldSlot->type = FIELD_TYPE_INTEGER; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value) | 
|  | { | 
|  | field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); | 
|  | if (!fieldSlot) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #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; | 
|  | } | 
|  |  | 
|  | bool CursorWindow::putNull(unsigned int row, unsigned int col) | 
|  | { | 
|  | field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); | 
|  | if (!fieldSlot) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #if WINDOW_STORAGE_INLINE_NUMERICS | 
|  | *valueOut = fieldSlot->data.l; | 
|  | #else | 
|  | *valueOut = copyOutLong(fieldSlot->data.buffer.offset); | 
|  | #endif | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #if WINDOW_STORAGE_INLINE_NUMERICS | 
|  | *valueOut = fieldSlot->data.d; | 
|  | #else | 
|  | *valueOut = copyOutDouble(fieldSlot->data.buffer.offset); | 
|  | #endif | 
|  | 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; | 
|  | } | 
|  |  | 
|  | }; // namespace android |