Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0dec77b authored by Mathias Agopian's avatar Mathias Agopian Committed by Android (Google) Code Review
Browse files

Merge "move CursorWindow from libbinder to libandroidfw"

parents 679154b3 89d353a4
Loading
Loading
Loading
Loading

include/binder/CursorWindow.h

deleted100644 → 0
+0 −193
Original line number Diff line number Diff line
/*
 * Copyright (C) 2006 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.
 */

#ifndef _ANDROID__DATABASE_WINDOW_H
#define _ANDROID__DATABASE_WINDOW_H

#include <cutils/log.h>
#include <stddef.h>
#include <stdint.h>

#include <binder/Parcel.h>
#include <utils/String8.h>

#if LOG_NDEBUG

#define IF_LOG_WINDOW() if (false)
#define LOG_WINDOW(...)

#else

#define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow")
#define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)

#endif

namespace android {

/**
 * This class stores a set of rows from a database in a buffer. The begining of the
 * 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
 * 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 {
    CursorWindow(const String8& name, int ashmemFd,
            void* data, size_t size, bool readOnly);

public:
    /* Field types. */
    enum {
        FIELD_TYPE_NULL = 0,
        FIELD_TYPE_INTEGER = 1,
        FIELD_TYPE_FLOAT = 2,
        FIELD_TYPE_STRING = 3,
        FIELD_TYPE_BLOB = 4,
    };

    /* 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;

        friend class CursorWindow;
    } __attribute((packed));

    ~CursorWindow();

    static status_t create(const String8& name, size_t size, CursorWindow** outCursorWindow);
    static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow);

    status_t writeToParcel(Parcel* parcel);

    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; }

    status_t clear();
    status_t setNumColumns(uint32_t numColumns);

    /**
     * Allocate a row slot and its directory.
     * The row is initialized will null entries for each field.
     */
    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

#endif
+0 −1
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@
sources := \
    Binder.cpp \
    BpBinder.cpp \
    CursorWindow.cpp \
    IInterface.cpp \
    IMemory.cpp \
    IPCThreadState.cpp \

libs/binder/CursorWindow.cpp

deleted100644 → 0
+0 −351
Original line number Diff line number Diff line
/*
 * 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 <cutils/ashmem.h>
#include <sys/mman.h>

#include <assert.h>
#include <string.h>
#include <stdlib.h>

namespace android {

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);
}

CursorWindow::~CursorWindow() {
    ::munmap(mData, mSize);
    ::close(mAshmemFd);
}

status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
    String8 ashmemName("CursorWindow: ");
    ashmemName.append(name);

    status_t result;
    int ashmemFd = ashmem_create_region(ashmemName.string(), size);
    if (ashmemFd < 0) {
        result = -errno;
    } else {
        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;
}

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;
}

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;

    RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
    firstChunk->nextChunkOffset = 0;
    return OK;
}

status_t CursorWindow::setNumColumns(uint32_t numColumns) {
    if (mReadOnly) {
        return INVALID_OPERATION;
    }

    uint32_t cur = mHeader->numColumns;
    if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
        ALOGE("Trying to go from %d columns to %d", cur, numColumns);
        return INVALID_OPERATION;
    }
    mHeader->numColumns = numColumns;
    return OK;
}

status_t CursorWindow::allocRow() {
    if (mReadOnly) {
        return INVALID_OPERATION;
    }

    // Fill in the row slot
    RowSlot* rowSlot = allocRowSlot();
    if (rowSlot == NULL) {
        return NO_MEMORY;
    }

    // Allocate the slots for the field directory
    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 NO_MEMORY;
    }
    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, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
    rowSlot->offset = fieldDirOffset;
    return OK;
}

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 = (~mHeader->freeOffset + 1) & 3;
    } else {
        padding = 0;
    }

    uint32_t offset = mHeader->freeOffset + padding;
    uint32_t nextFreeOffset = offset + size;
    if (nextFreeOffset > mSize) {
        ALOGW("Window is full: requested allocation %d bytes, "
                "free space %d bytes, window size %d bytes",
                size, freeSpace(), mSize);
        return 0;
    }

    mHeader->freeOffset = nextFreeOffset;
    return offset;
}

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 &chunk->slots[chunkPos];
}

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;
            }
        }
        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
        chunk->nextChunkOffset = 0;
        chunkPos = 0;
    }
    mHeader->numRows += 1;
    return &chunk->slots[chunkPos];
}

CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
    if (row >= mHeader->numRows || column >= mHeader->numColumns) {
        ALOGE("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) {
        ALOGE("Failed to find rowSlot for row %d.", row);
        return NULL;
    }
    FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
    return &fieldDir[column];
}

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);
}

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);
}

status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
        const void* value, size_t size, int32_t type) {
    if (mReadOnly) {
        return INVALID_OPERATION;
    }

    FieldSlot* fieldSlot = getFieldSlot(row, column);
    if (!fieldSlot) {
        return BAD_VALUE;
    }

    uint32_t offset = alloc(size);
    if (!offset) {
        return NO_MEMORY;
    }

    memcpy(offsetToPtr(offset), value, size);

    fieldSlot->type = type;
    fieldSlot->data.buffer.offset = offset;
    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;
    fieldSlot->data.l = value;
    return OK;
}

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 BAD_VALUE;
    }

    fieldSlot->type = FIELD_TYPE_FLOAT;
    fieldSlot->data.d = value;
    return OK;
}

status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
    if (mReadOnly) {
        return INVALID_OPERATION;
    }

    FieldSlot* fieldSlot = getFieldSlot(row, column);
    if (!fieldSlot) {
        return BAD_VALUE;
    }

    fieldSlot->type = FIELD_TYPE_NULL;
    fieldSlot->data.buffer.offset = 0;
    fieldSlot->data.buffer.size = 0;
    return OK;
}

}; // namespace android