CursorWindow.cpp revision 49d2b1864c3dfec6faff74d67cb2527a8f1af5a8
1/*
2 * Copyright (C) 2006-2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#undef LOG_TAG
18#define LOG_TAG "CursorWindow"
19
20#include <utils/Log.h>
21#include <androidfw/CursorWindow.h>
22
23#include <cutils/ashmem.h>
24#include <sys/mman.h>
25
26#include <assert.h>
27#include <string.h>
28#include <stdlib.h>
29
30namespace android {
31
32CursorWindow::CursorWindow(const String8& name, int ashmemFd,
33        void* data, size_t size, bool readOnly) :
34        mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
35    mHeader = static_cast<Header*>(mData);
36}
37
38CursorWindow::~CursorWindow() {
39    ::munmap(mData, mSize);
40    ::close(mAshmemFd);
41}
42
43status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
44    String8 ashmemName("CursorWindow: ");
45    ashmemName.append(name);
46
47    status_t result;
48    int ashmemFd = ashmem_create_region(ashmemName.string(), size);
49    if (ashmemFd < 0) {
50        result = -errno;
51    } else {
52        result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
53        if (result >= 0) {
54            void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
55            if (data == MAP_FAILED) {
56                result = -errno;
57            } else {
58                result = ashmem_set_prot_region(ashmemFd, PROT_READ);
59                if (result >= 0) {
60                    CursorWindow* window = new CursorWindow(name, ashmemFd,
61                            data, size, false /*readOnly*/);
62                    result = window->clear();
63                    if (!result) {
64                        LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
65                                "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
66                                window->mHeader->freeOffset,
67                                window->mHeader->numRows,
68                                window->mHeader->numColumns,
69                                window->mSize, window->mData);
70                        *outCursorWindow = window;
71                        return OK;
72                    }
73                    delete window;
74                }
75            }
76            ::munmap(data, size);
77        }
78        ::close(ashmemFd);
79    }
80    *outCursorWindow = NULL;
81    return result;
82}
83
84status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
85    String8 name = parcel->readString8();
86
87    status_t result;
88    int ashmemFd = parcel->readFileDescriptor();
89    if (ashmemFd == int(BAD_TYPE)) {
90        result = BAD_TYPE;
91    } else {
92        ssize_t size = ashmem_get_size_region(ashmemFd);
93        if (size < 0) {
94            result = UNKNOWN_ERROR;
95        } else {
96            int dupAshmemFd = ::dup(ashmemFd);
97            if (dupAshmemFd < 0) {
98                result = -errno;
99            } else {
100                void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
101                if (data == MAP_FAILED) {
102                    result = -errno;
103                } else {
104                    CursorWindow* window = new CursorWindow(name, dupAshmemFd,
105                            data, size, true /*readOnly*/);
106                    LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
107                            "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
108                            window->mHeader->freeOffset,
109                            window->mHeader->numRows,
110                            window->mHeader->numColumns,
111                            window->mSize, window->mData);
112                    *outCursorWindow = window;
113                    return OK;
114                }
115                ::close(dupAshmemFd);
116            }
117        }
118    }
119    *outCursorWindow = NULL;
120    return result;
121}
122
123status_t CursorWindow::writeToParcel(Parcel* parcel) {
124    status_t status = parcel->writeString8(mName);
125    if (!status) {
126        status = parcel->writeDupFileDescriptor(mAshmemFd);
127    }
128    return status;
129}
130
131status_t CursorWindow::clear() {
132    if (mReadOnly) {
133        return INVALID_OPERATION;
134    }
135
136    mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
137    mHeader->firstChunkOffset = sizeof(Header);
138    mHeader->numRows = 0;
139    mHeader->numColumns = 0;
140
141    RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
142    firstChunk->nextChunkOffset = 0;
143    return OK;
144}
145
146status_t CursorWindow::setNumColumns(uint32_t numColumns) {
147    if (mReadOnly) {
148        return INVALID_OPERATION;
149    }
150
151    uint32_t cur = mHeader->numColumns;
152    if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
153        ALOGE("Trying to go from %d columns to %d", cur, numColumns);
154        return INVALID_OPERATION;
155    }
156    mHeader->numColumns = numColumns;
157    return OK;
158}
159
160status_t CursorWindow::allocRow() {
161    if (mReadOnly) {
162        return INVALID_OPERATION;
163    }
164
165    // Fill in the row slot
166    RowSlot* rowSlot = allocRowSlot();
167    if (rowSlot == NULL) {
168        return NO_MEMORY;
169    }
170
171    // Allocate the slots for the field directory
172    size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
173    uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
174    if (!fieldDirOffset) {
175        mHeader->numRows--;
176        LOG_WINDOW("The row failed, so back out the new row accounting "
177                "from allocRowSlot %d", mHeader->numRows);
178        return NO_MEMORY;
179    }
180    FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
181    memset(fieldDir, 0, fieldDirSize);
182
183    LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n",
184            mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
185    rowSlot->offset = fieldDirOffset;
186    return OK;
187}
188
189status_t CursorWindow::freeLastRow() {
190    if (mReadOnly) {
191        return INVALID_OPERATION;
192    }
193
194    if (mHeader->numRows > 0) {
195        mHeader->numRows--;
196    }
197    return OK;
198}
199
200uint32_t CursorWindow::alloc(size_t size, bool aligned) {
201    uint32_t padding;
202    if (aligned) {
203        // 4 byte alignment
204        padding = (~mHeader->freeOffset + 1) & 3;
205    } else {
206        padding = 0;
207    }
208
209    uint32_t offset = mHeader->freeOffset + padding;
210    uint32_t nextFreeOffset = offset + size;
211    if (nextFreeOffset > mSize) {
212        ALOGW("Window is full: requested allocation %d bytes, "
213                "free space %d bytes, window size %d bytes",
214                size, freeSpace(), mSize);
215        return 0;
216    }
217
218    mHeader->freeOffset = nextFreeOffset;
219    return offset;
220}
221
222CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
223    uint32_t chunkPos = row;
224    RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
225            offsetToPtr(mHeader->firstChunkOffset));
226    while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
227        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
228        chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
229    }
230    return &chunk->slots[chunkPos];
231}
232
233CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
234    uint32_t chunkPos = mHeader->numRows;
235    RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
236            offsetToPtr(mHeader->firstChunkOffset));
237    while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
238        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
239        chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
240    }
241    if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
242        if (!chunk->nextChunkOffset) {
243            chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
244            if (!chunk->nextChunkOffset) {
245                return NULL;
246            }
247        }
248        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
249        chunk->nextChunkOffset = 0;
250        chunkPos = 0;
251    }
252    mHeader->numRows += 1;
253    return &chunk->slots[chunkPos];
254}
255
256CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
257    if (row >= mHeader->numRows || column >= mHeader->numColumns) {
258        ALOGE("Failed to read row %d, column %d from a CursorWindow which "
259                "has %d rows, %d columns.",
260                row, column, mHeader->numRows, mHeader->numColumns);
261        return NULL;
262    }
263    RowSlot* rowSlot = getRowSlot(row);
264    if (!rowSlot) {
265        ALOGE("Failed to find rowSlot for row %d.", row);
266        return NULL;
267    }
268    FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
269    return &fieldDir[column];
270}
271
272status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
273    return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
274}
275
276status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
277        size_t sizeIncludingNull) {
278    return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
279}
280
281status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
282        const void* value, size_t size, int32_t type) {
283    if (mReadOnly) {
284        return INVALID_OPERATION;
285    }
286
287    FieldSlot* fieldSlot = getFieldSlot(row, column);
288    if (!fieldSlot) {
289        return BAD_VALUE;
290    }
291
292    uint32_t offset = alloc(size);
293    if (!offset) {
294        return NO_MEMORY;
295    }
296
297    memcpy(offsetToPtr(offset), value, size);
298
299    fieldSlot->type = type;
300    fieldSlot->data.buffer.offset = offset;
301    fieldSlot->data.buffer.size = size;
302    return OK;
303}
304
305status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
306    if (mReadOnly) {
307        return INVALID_OPERATION;
308    }
309
310    FieldSlot* fieldSlot = getFieldSlot(row, column);
311    if (!fieldSlot) {
312        return BAD_VALUE;
313    }
314
315    fieldSlot->type = FIELD_TYPE_INTEGER;
316    fieldSlot->data.l = value;
317    return OK;
318}
319
320status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
321    if (mReadOnly) {
322        return INVALID_OPERATION;
323    }
324
325    FieldSlot* fieldSlot = getFieldSlot(row, column);
326    if (!fieldSlot) {
327        return BAD_VALUE;
328    }
329
330    fieldSlot->type = FIELD_TYPE_FLOAT;
331    fieldSlot->data.d = value;
332    return OK;
333}
334
335status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
336    if (mReadOnly) {
337        return INVALID_OPERATION;
338    }
339
340    FieldSlot* fieldSlot = getFieldSlot(row, column);
341    if (!fieldSlot) {
342        return BAD_VALUE;
343    }
344
345    fieldSlot->type = FIELD_TYPE_NULL;
346    fieldSlot->data.buffer.offset = 0;
347    fieldSlot->data.buffer.size = 0;
348    return OK;
349}
350
351}; // namespace android
352