1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2006-2007 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #define LOG_TAG "CursorWindow"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <androidfw/CursorWindow.h>
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard Worker #include <sys/mman.h>
22*d57664e9SAndroid Build Coastguard Worker
23*d57664e9SAndroid Build Coastguard Worker #include "android-base/logging.h"
24*d57664e9SAndroid Build Coastguard Worker #include "cutils/ashmem.h"
25*d57664e9SAndroid Build Coastguard Worker
26*d57664e9SAndroid Build Coastguard Worker namespace android {
27*d57664e9SAndroid Build Coastguard Worker
28*d57664e9SAndroid Build Coastguard Worker /**
29*d57664e9SAndroid Build Coastguard Worker * By default windows are lightweight inline allocations of this size;
30*d57664e9SAndroid Build Coastguard Worker * they're only inflated to ashmem regions when more space is needed.
31*d57664e9SAndroid Build Coastguard Worker */
32*d57664e9SAndroid Build Coastguard Worker static constexpr const size_t kInlineSize = 16384;
33*d57664e9SAndroid Build Coastguard Worker
34*d57664e9SAndroid Build Coastguard Worker static constexpr const size_t kSlotShift = 4;
35*d57664e9SAndroid Build Coastguard Worker static constexpr const size_t kSlotSizeBytes = 1 << kSlotShift;
36*d57664e9SAndroid Build Coastguard Worker
CursorWindow()37*d57664e9SAndroid Build Coastguard Worker CursorWindow::CursorWindow() {
38*d57664e9SAndroid Build Coastguard Worker }
39*d57664e9SAndroid Build Coastguard Worker
~CursorWindow()40*d57664e9SAndroid Build Coastguard Worker CursorWindow::~CursorWindow() {
41*d57664e9SAndroid Build Coastguard Worker if (mAshmemFd >= 0) {
42*d57664e9SAndroid Build Coastguard Worker ::munmap(mData, mSize);
43*d57664e9SAndroid Build Coastguard Worker ::close(mAshmemFd);
44*d57664e9SAndroid Build Coastguard Worker } else {
45*d57664e9SAndroid Build Coastguard Worker free(mData);
46*d57664e9SAndroid Build Coastguard Worker }
47*d57664e9SAndroid Build Coastguard Worker }
48*d57664e9SAndroid Build Coastguard Worker
create(const String8 & name,size_t inflatedSize,CursorWindow ** outWindow)49*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWindow **outWindow) {
50*d57664e9SAndroid Build Coastguard Worker *outWindow = nullptr;
51*d57664e9SAndroid Build Coastguard Worker
52*d57664e9SAndroid Build Coastguard Worker CursorWindow* window = new CursorWindow();
53*d57664e9SAndroid Build Coastguard Worker if (!window) goto fail;
54*d57664e9SAndroid Build Coastguard Worker
55*d57664e9SAndroid Build Coastguard Worker window->mName = name;
56*d57664e9SAndroid Build Coastguard Worker window->mSize = std::min(kInlineSize, inflatedSize);
57*d57664e9SAndroid Build Coastguard Worker window->mInflatedSize = inflatedSize;
58*d57664e9SAndroid Build Coastguard Worker window->mData = malloc(window->mSize);
59*d57664e9SAndroid Build Coastguard Worker if (!window->mData) goto fail;
60*d57664e9SAndroid Build Coastguard Worker window->mReadOnly = false;
61*d57664e9SAndroid Build Coastguard Worker
62*d57664e9SAndroid Build Coastguard Worker window->clear();
63*d57664e9SAndroid Build Coastguard Worker window->updateSlotsData();
64*d57664e9SAndroid Build Coastguard Worker
65*d57664e9SAndroid Build Coastguard Worker *outWindow = window;
66*d57664e9SAndroid Build Coastguard Worker return OK;
67*d57664e9SAndroid Build Coastguard Worker
68*d57664e9SAndroid Build Coastguard Worker fail:
69*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Failed create";
70*d57664e9SAndroid Build Coastguard Worker fail_silent:
71*d57664e9SAndroid Build Coastguard Worker delete window;
72*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
73*d57664e9SAndroid Build Coastguard Worker }
74*d57664e9SAndroid Build Coastguard Worker
maybeInflate()75*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::maybeInflate() {
76*d57664e9SAndroid Build Coastguard Worker int ashmemFd = 0;
77*d57664e9SAndroid Build Coastguard Worker void* newData = nullptr;
78*d57664e9SAndroid Build Coastguard Worker
79*d57664e9SAndroid Build Coastguard Worker // Bail early when we can't expand any further
80*d57664e9SAndroid Build Coastguard Worker if (mReadOnly || mSize == mInflatedSize) {
81*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
82*d57664e9SAndroid Build Coastguard Worker }
83*d57664e9SAndroid Build Coastguard Worker
84*d57664e9SAndroid Build Coastguard Worker String8 ashmemName("CursorWindow: ");
85*d57664e9SAndroid Build Coastguard Worker ashmemName.append(mName);
86*d57664e9SAndroid Build Coastguard Worker
87*d57664e9SAndroid Build Coastguard Worker ashmemFd = ashmem_create_region(ashmemName.c_str(), mInflatedSize);
88*d57664e9SAndroid Build Coastguard Worker if (ashmemFd < 0) {
89*d57664e9SAndroid Build Coastguard Worker PLOG(ERROR) << "Failed ashmem_create_region";
90*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
91*d57664e9SAndroid Build Coastguard Worker }
92*d57664e9SAndroid Build Coastguard Worker
93*d57664e9SAndroid Build Coastguard Worker if (ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE) < 0) {
94*d57664e9SAndroid Build Coastguard Worker PLOG(ERROR) << "Failed ashmem_set_prot_region";
95*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
96*d57664e9SAndroid Build Coastguard Worker }
97*d57664e9SAndroid Build Coastguard Worker
98*d57664e9SAndroid Build Coastguard Worker newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
99*d57664e9SAndroid Build Coastguard Worker if (newData == MAP_FAILED) {
100*d57664e9SAndroid Build Coastguard Worker PLOG(ERROR) << "Failed mmap";
101*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
102*d57664e9SAndroid Build Coastguard Worker }
103*d57664e9SAndroid Build Coastguard Worker
104*d57664e9SAndroid Build Coastguard Worker if (ashmem_set_prot_region(ashmemFd, PROT_READ) < 0) {
105*d57664e9SAndroid Build Coastguard Worker PLOG(ERROR) << "Failed ashmem_set_prot_region";
106*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
107*d57664e9SAndroid Build Coastguard Worker }
108*d57664e9SAndroid Build Coastguard Worker
109*d57664e9SAndroid Build Coastguard Worker {
110*d57664e9SAndroid Build Coastguard Worker // Migrate existing contents into new ashmem region
111*d57664e9SAndroid Build Coastguard Worker uint32_t slotsSize = sizeOfSlots();
112*d57664e9SAndroid Build Coastguard Worker uint32_t newSlotsOffset = mInflatedSize - slotsSize;
113*d57664e9SAndroid Build Coastguard Worker memcpy(static_cast<uint8_t*>(newData),
114*d57664e9SAndroid Build Coastguard Worker static_cast<uint8_t*>(mData), mAllocOffset);
115*d57664e9SAndroid Build Coastguard Worker memcpy(static_cast<uint8_t*>(newData) + newSlotsOffset,
116*d57664e9SAndroid Build Coastguard Worker static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
117*d57664e9SAndroid Build Coastguard Worker
118*d57664e9SAndroid Build Coastguard Worker free(mData);
119*d57664e9SAndroid Build Coastguard Worker mAshmemFd = ashmemFd;
120*d57664e9SAndroid Build Coastguard Worker mData = newData;
121*d57664e9SAndroid Build Coastguard Worker mSize = mInflatedSize;
122*d57664e9SAndroid Build Coastguard Worker mSlotsOffset = newSlotsOffset;
123*d57664e9SAndroid Build Coastguard Worker
124*d57664e9SAndroid Build Coastguard Worker updateSlotsData();
125*d57664e9SAndroid Build Coastguard Worker }
126*d57664e9SAndroid Build Coastguard Worker
127*d57664e9SAndroid Build Coastguard Worker LOG(DEBUG) << "Inflated: " << this->toString();
128*d57664e9SAndroid Build Coastguard Worker return OK;
129*d57664e9SAndroid Build Coastguard Worker
130*d57664e9SAndroid Build Coastguard Worker fail:
131*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Failed maybeInflate";
132*d57664e9SAndroid Build Coastguard Worker fail_silent:
133*d57664e9SAndroid Build Coastguard Worker ::munmap(newData, mInflatedSize);
134*d57664e9SAndroid Build Coastguard Worker ::close(ashmemFd);
135*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
136*d57664e9SAndroid Build Coastguard Worker }
137*d57664e9SAndroid Build Coastguard Worker
createFromParcel(Parcel * parcel,CursorWindow ** outWindow)138*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow) {
139*d57664e9SAndroid Build Coastguard Worker *outWindow = nullptr;
140*d57664e9SAndroid Build Coastguard Worker
141*d57664e9SAndroid Build Coastguard Worker CursorWindow* window = new CursorWindow();
142*d57664e9SAndroid Build Coastguard Worker if (!window) goto fail;
143*d57664e9SAndroid Build Coastguard Worker
144*d57664e9SAndroid Build Coastguard Worker if (parcel->readString8(&window->mName)) goto fail;
145*d57664e9SAndroid Build Coastguard Worker if (parcel->readUint32(&window->mNumRows)) goto fail;
146*d57664e9SAndroid Build Coastguard Worker if (parcel->readUint32(&window->mNumColumns)) goto fail;
147*d57664e9SAndroid Build Coastguard Worker if (parcel->readUint32(&window->mSize)) goto fail;
148*d57664e9SAndroid Build Coastguard Worker
149*d57664e9SAndroid Build Coastguard Worker if ((window->mNumRows * window->mNumColumns * kSlotSizeBytes) > window->mSize) {
150*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Unexpected size " << window->mSize << " for " << window->mNumRows
151*d57664e9SAndroid Build Coastguard Worker << " rows and " << window->mNumColumns << " columns";
152*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
153*d57664e9SAndroid Build Coastguard Worker }
154*d57664e9SAndroid Build Coastguard Worker
155*d57664e9SAndroid Build Coastguard Worker bool isAshmem;
156*d57664e9SAndroid Build Coastguard Worker if (parcel->readBool(&isAshmem)) goto fail;
157*d57664e9SAndroid Build Coastguard Worker if (isAshmem) {
158*d57664e9SAndroid Build Coastguard Worker int tempFd = parcel->readFileDescriptor();
159*d57664e9SAndroid Build Coastguard Worker if (tempFd < 0) {
160*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Failed readFileDescriptor";
161*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
162*d57664e9SAndroid Build Coastguard Worker }
163*d57664e9SAndroid Build Coastguard Worker
164*d57664e9SAndroid Build Coastguard Worker tempFd = ::fcntl(tempFd, F_DUPFD_CLOEXEC, 0);
165*d57664e9SAndroid Build Coastguard Worker if (tempFd < 0) {
166*d57664e9SAndroid Build Coastguard Worker PLOG(ERROR) << "Failed F_DUPFD_CLOEXEC";
167*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
168*d57664e9SAndroid Build Coastguard Worker }
169*d57664e9SAndroid Build Coastguard Worker
170*d57664e9SAndroid Build Coastguard Worker window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, tempFd, 0);
171*d57664e9SAndroid Build Coastguard Worker if (window->mData == MAP_FAILED) {
172*d57664e9SAndroid Build Coastguard Worker ::close(tempFd);
173*d57664e9SAndroid Build Coastguard Worker PLOG(ERROR) << "Failed mmap";
174*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
175*d57664e9SAndroid Build Coastguard Worker }
176*d57664e9SAndroid Build Coastguard Worker
177*d57664e9SAndroid Build Coastguard Worker window->mAshmemFd = tempFd;
178*d57664e9SAndroid Build Coastguard Worker
179*d57664e9SAndroid Build Coastguard Worker } else {
180*d57664e9SAndroid Build Coastguard Worker window->mAshmemFd = -1;
181*d57664e9SAndroid Build Coastguard Worker
182*d57664e9SAndroid Build Coastguard Worker if (window->mSize > kInlineSize) {
183*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Unexpected size " << window->mSize << " for inline window";
184*d57664e9SAndroid Build Coastguard Worker goto fail_silent;
185*d57664e9SAndroid Build Coastguard Worker }
186*d57664e9SAndroid Build Coastguard Worker
187*d57664e9SAndroid Build Coastguard Worker window->mData = malloc(window->mSize);
188*d57664e9SAndroid Build Coastguard Worker if (!window->mData) goto fail;
189*d57664e9SAndroid Build Coastguard Worker
190*d57664e9SAndroid Build Coastguard Worker if (parcel->read(window->mData, window->mSize)) goto fail;
191*d57664e9SAndroid Build Coastguard Worker }
192*d57664e9SAndroid Build Coastguard Worker
193*d57664e9SAndroid Build Coastguard Worker // We just came from a remote source, so we're read-only
194*d57664e9SAndroid Build Coastguard Worker // and we can't inflate ourselves
195*d57664e9SAndroid Build Coastguard Worker window->mInflatedSize = window->mSize;
196*d57664e9SAndroid Build Coastguard Worker window->mReadOnly = true;
197*d57664e9SAndroid Build Coastguard Worker
198*d57664e9SAndroid Build Coastguard Worker window->updateSlotsData();
199*d57664e9SAndroid Build Coastguard Worker
200*d57664e9SAndroid Build Coastguard Worker LOG(DEBUG) << "Created from parcel: " << window->toString();
201*d57664e9SAndroid Build Coastguard Worker *outWindow = window;
202*d57664e9SAndroid Build Coastguard Worker return OK;
203*d57664e9SAndroid Build Coastguard Worker
204*d57664e9SAndroid Build Coastguard Worker fail:
205*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Failed createFromParcel";
206*d57664e9SAndroid Build Coastguard Worker fail_silent:
207*d57664e9SAndroid Build Coastguard Worker delete window;
208*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
209*d57664e9SAndroid Build Coastguard Worker }
210*d57664e9SAndroid Build Coastguard Worker
writeToParcel(Parcel * parcel)211*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::writeToParcel(Parcel* parcel) {
212*d57664e9SAndroid Build Coastguard Worker LOG(DEBUG) << "Writing to parcel: " << this->toString();
213*d57664e9SAndroid Build Coastguard Worker
214*d57664e9SAndroid Build Coastguard Worker if (parcel->writeString8(mName)) goto fail;
215*d57664e9SAndroid Build Coastguard Worker if (parcel->writeUint32(mNumRows)) goto fail;
216*d57664e9SAndroid Build Coastguard Worker if (parcel->writeUint32(mNumColumns)) goto fail;
217*d57664e9SAndroid Build Coastguard Worker if (mAshmemFd != -1) {
218*d57664e9SAndroid Build Coastguard Worker if (parcel->writeUint32(mSize)) goto fail;
219*d57664e9SAndroid Build Coastguard Worker if (parcel->writeBool(true)) goto fail;
220*d57664e9SAndroid Build Coastguard Worker if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail;
221*d57664e9SAndroid Build Coastguard Worker } else {
222*d57664e9SAndroid Build Coastguard Worker // Since we know we're going to be read-only on the remote side,
223*d57664e9SAndroid Build Coastguard Worker // we can compact ourselves on the wire.
224*d57664e9SAndroid Build Coastguard Worker size_t slotsSize = sizeOfSlots();
225*d57664e9SAndroid Build Coastguard Worker size_t compactedSize = sizeInUse();
226*d57664e9SAndroid Build Coastguard Worker if (parcel->writeUint32(compactedSize)) goto fail;
227*d57664e9SAndroid Build Coastguard Worker if (parcel->writeBool(false)) goto fail;
228*d57664e9SAndroid Build Coastguard Worker void* dest = parcel->writeInplace(compactedSize);
229*d57664e9SAndroid Build Coastguard Worker if (!dest) goto fail;
230*d57664e9SAndroid Build Coastguard Worker memcpy(static_cast<uint8_t*>(dest),
231*d57664e9SAndroid Build Coastguard Worker static_cast<uint8_t*>(mData), mAllocOffset);
232*d57664e9SAndroid Build Coastguard Worker memcpy(static_cast<uint8_t*>(dest) + compactedSize - slotsSize,
233*d57664e9SAndroid Build Coastguard Worker static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
234*d57664e9SAndroid Build Coastguard Worker }
235*d57664e9SAndroid Build Coastguard Worker return OK;
236*d57664e9SAndroid Build Coastguard Worker
237*d57664e9SAndroid Build Coastguard Worker fail:
238*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Failed writeToParcel";
239*d57664e9SAndroid Build Coastguard Worker fail_silent:
240*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
241*d57664e9SAndroid Build Coastguard Worker }
242*d57664e9SAndroid Build Coastguard Worker
clear()243*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::clear() {
244*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
245*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
246*d57664e9SAndroid Build Coastguard Worker }
247*d57664e9SAndroid Build Coastguard Worker mAllocOffset = 0;
248*d57664e9SAndroid Build Coastguard Worker mSlotsOffset = mSize;
249*d57664e9SAndroid Build Coastguard Worker mNumRows = 0;
250*d57664e9SAndroid Build Coastguard Worker mNumColumns = 0;
251*d57664e9SAndroid Build Coastguard Worker return OK;
252*d57664e9SAndroid Build Coastguard Worker }
253*d57664e9SAndroid Build Coastguard Worker
updateSlotsData()254*d57664e9SAndroid Build Coastguard Worker void CursorWindow::updateSlotsData() {
255*d57664e9SAndroid Build Coastguard Worker mSlotsStart = static_cast<uint8_t*>(mData) + mSize - kSlotSizeBytes;
256*d57664e9SAndroid Build Coastguard Worker mSlotsEnd = static_cast<uint8_t*>(mData) + mSlotsOffset;
257*d57664e9SAndroid Build Coastguard Worker }
258*d57664e9SAndroid Build Coastguard Worker
offsetToPtr(uint32_t offset,uint32_t bufferSize=0)259*d57664e9SAndroid Build Coastguard Worker void* CursorWindow::offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
260*d57664e9SAndroid Build Coastguard Worker if (offset > mSize) {
261*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Offset " << offset
262*d57664e9SAndroid Build Coastguard Worker << " out of bounds, max value " << mSize;
263*d57664e9SAndroid Build Coastguard Worker return nullptr;
264*d57664e9SAndroid Build Coastguard Worker }
265*d57664e9SAndroid Build Coastguard Worker if (offset + bufferSize > mSize) {
266*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "End offset " << (offset + bufferSize)
267*d57664e9SAndroid Build Coastguard Worker << " out of bounds, max value " << mSize;
268*d57664e9SAndroid Build Coastguard Worker return nullptr;
269*d57664e9SAndroid Build Coastguard Worker }
270*d57664e9SAndroid Build Coastguard Worker return static_cast<uint8_t*>(mData) + offset;
271*d57664e9SAndroid Build Coastguard Worker }
272*d57664e9SAndroid Build Coastguard Worker
offsetFromPtr(void * ptr)273*d57664e9SAndroid Build Coastguard Worker uint32_t CursorWindow::offsetFromPtr(void* ptr) {
274*d57664e9SAndroid Build Coastguard Worker return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
275*d57664e9SAndroid Build Coastguard Worker }
276*d57664e9SAndroid Build Coastguard Worker
setNumColumns(uint32_t numColumns)277*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::setNumColumns(uint32_t numColumns) {
278*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
279*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
280*d57664e9SAndroid Build Coastguard Worker }
281*d57664e9SAndroid Build Coastguard Worker uint32_t cur = mNumColumns;
282*d57664e9SAndroid Build Coastguard Worker if ((cur > 0 || mNumRows > 0) && cur != numColumns) {
283*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Trying to go from " << cur << " columns to " << numColumns;
284*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
285*d57664e9SAndroid Build Coastguard Worker }
286*d57664e9SAndroid Build Coastguard Worker mNumColumns = numColumns;
287*d57664e9SAndroid Build Coastguard Worker return OK;
288*d57664e9SAndroid Build Coastguard Worker }
289*d57664e9SAndroid Build Coastguard Worker
allocRow()290*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::allocRow() {
291*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
292*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
293*d57664e9SAndroid Build Coastguard Worker }
294*d57664e9SAndroid Build Coastguard Worker size_t size = mNumColumns * kSlotSizeBytes;
295*d57664e9SAndroid Build Coastguard Worker int32_t newOffset = mSlotsOffset - size;
296*d57664e9SAndroid Build Coastguard Worker if (newOffset < (int32_t) mAllocOffset) {
297*d57664e9SAndroid Build Coastguard Worker maybeInflate();
298*d57664e9SAndroid Build Coastguard Worker newOffset = mSlotsOffset - size;
299*d57664e9SAndroid Build Coastguard Worker if (newOffset < (int32_t) mAllocOffset) {
300*d57664e9SAndroid Build Coastguard Worker return NO_MEMORY;
301*d57664e9SAndroid Build Coastguard Worker }
302*d57664e9SAndroid Build Coastguard Worker }
303*d57664e9SAndroid Build Coastguard Worker memset(offsetToPtr(newOffset), 0, size);
304*d57664e9SAndroid Build Coastguard Worker mSlotsOffset = newOffset;
305*d57664e9SAndroid Build Coastguard Worker updateSlotsData();
306*d57664e9SAndroid Build Coastguard Worker mNumRows++;
307*d57664e9SAndroid Build Coastguard Worker return OK;
308*d57664e9SAndroid Build Coastguard Worker }
309*d57664e9SAndroid Build Coastguard Worker
freeLastRow()310*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::freeLastRow() {
311*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
312*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
313*d57664e9SAndroid Build Coastguard Worker }
314*d57664e9SAndroid Build Coastguard Worker size_t size = mNumColumns * kSlotSizeBytes;
315*d57664e9SAndroid Build Coastguard Worker size_t newOffset = mSlotsOffset + size;
316*d57664e9SAndroid Build Coastguard Worker if (newOffset > mSize) {
317*d57664e9SAndroid Build Coastguard Worker return NO_MEMORY;
318*d57664e9SAndroid Build Coastguard Worker }
319*d57664e9SAndroid Build Coastguard Worker mSlotsOffset = newOffset;
320*d57664e9SAndroid Build Coastguard Worker updateSlotsData();
321*d57664e9SAndroid Build Coastguard Worker mNumRows--;
322*d57664e9SAndroid Build Coastguard Worker return OK;
323*d57664e9SAndroid Build Coastguard Worker }
324*d57664e9SAndroid Build Coastguard Worker
alloc(size_t size,uint32_t * outOffset)325*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::alloc(size_t size, uint32_t* outOffset) {
326*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
327*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
328*d57664e9SAndroid Build Coastguard Worker }
329*d57664e9SAndroid Build Coastguard Worker size_t alignedSize = (size + 3) & ~3;
330*d57664e9SAndroid Build Coastguard Worker size_t newOffset = mAllocOffset + alignedSize;
331*d57664e9SAndroid Build Coastguard Worker if (newOffset > mSlotsOffset) {
332*d57664e9SAndroid Build Coastguard Worker maybeInflate();
333*d57664e9SAndroid Build Coastguard Worker newOffset = mAllocOffset + alignedSize;
334*d57664e9SAndroid Build Coastguard Worker if (newOffset > mSlotsOffset) {
335*d57664e9SAndroid Build Coastguard Worker return NO_MEMORY;
336*d57664e9SAndroid Build Coastguard Worker }
337*d57664e9SAndroid Build Coastguard Worker }
338*d57664e9SAndroid Build Coastguard Worker *outOffset = mAllocOffset;
339*d57664e9SAndroid Build Coastguard Worker mAllocOffset = newOffset;
340*d57664e9SAndroid Build Coastguard Worker return OK;
341*d57664e9SAndroid Build Coastguard Worker }
342*d57664e9SAndroid Build Coastguard Worker
getFieldSlot(uint32_t row,uint32_t column)343*d57664e9SAndroid Build Coastguard Worker CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
344*d57664e9SAndroid Build Coastguard Worker // This is carefully tuned to use as few cycles as
345*d57664e9SAndroid Build Coastguard Worker // possible, since this is an extremely hot code path;
346*d57664e9SAndroid Build Coastguard Worker // see CursorWindow_bench.cpp for more details
347*d57664e9SAndroid Build Coastguard Worker void *result = static_cast<uint8_t*>(mSlotsStart)
348*d57664e9SAndroid Build Coastguard Worker - (((row * mNumColumns) + column) << kSlotShift);
349*d57664e9SAndroid Build Coastguard Worker if (result < mSlotsEnd || result > mSlotsStart || column >= mNumColumns) {
350*d57664e9SAndroid Build Coastguard Worker LOG(ERROR) << "Failed to read row " << row << ", column " << column
351*d57664e9SAndroid Build Coastguard Worker << " from a window with " << mNumRows << " rows, " << mNumColumns << " columns";
352*d57664e9SAndroid Build Coastguard Worker return nullptr;
353*d57664e9SAndroid Build Coastguard Worker } else {
354*d57664e9SAndroid Build Coastguard Worker return static_cast<FieldSlot*>(result);
355*d57664e9SAndroid Build Coastguard Worker }
356*d57664e9SAndroid Build Coastguard Worker }
357*d57664e9SAndroid Build Coastguard Worker
putBlob(uint32_t row,uint32_t column,const void * value,size_t size)358*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
359*d57664e9SAndroid Build Coastguard Worker return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
360*d57664e9SAndroid Build Coastguard Worker }
361*d57664e9SAndroid Build Coastguard Worker
putString(uint32_t row,uint32_t column,const char * value,size_t sizeIncludingNull)362*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
363*d57664e9SAndroid Build Coastguard Worker size_t sizeIncludingNull) {
364*d57664e9SAndroid Build Coastguard Worker return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
365*d57664e9SAndroid Build Coastguard Worker }
366*d57664e9SAndroid Build Coastguard Worker
putBlobOrString(uint32_t row,uint32_t column,const void * value,size_t size,int32_t type)367*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
368*d57664e9SAndroid Build Coastguard Worker const void* value, size_t size, int32_t type) {
369*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
370*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
371*d57664e9SAndroid Build Coastguard Worker }
372*d57664e9SAndroid Build Coastguard Worker
373*d57664e9SAndroid Build Coastguard Worker FieldSlot* fieldSlot = getFieldSlot(row, column);
374*d57664e9SAndroid Build Coastguard Worker if (!fieldSlot) {
375*d57664e9SAndroid Build Coastguard Worker return BAD_VALUE;
376*d57664e9SAndroid Build Coastguard Worker }
377*d57664e9SAndroid Build Coastguard Worker
378*d57664e9SAndroid Build Coastguard Worker uint32_t offset;
379*d57664e9SAndroid Build Coastguard Worker if (alloc(size, &offset)) {
380*d57664e9SAndroid Build Coastguard Worker return NO_MEMORY;
381*d57664e9SAndroid Build Coastguard Worker }
382*d57664e9SAndroid Build Coastguard Worker
383*d57664e9SAndroid Build Coastguard Worker memcpy(offsetToPtr(offset), value, size);
384*d57664e9SAndroid Build Coastguard Worker
385*d57664e9SAndroid Build Coastguard Worker fieldSlot = getFieldSlot(row, column);
386*d57664e9SAndroid Build Coastguard Worker fieldSlot->type = type;
387*d57664e9SAndroid Build Coastguard Worker fieldSlot->data.buffer.offset = offset;
388*d57664e9SAndroid Build Coastguard Worker fieldSlot->data.buffer.size = size;
389*d57664e9SAndroid Build Coastguard Worker return OK;
390*d57664e9SAndroid Build Coastguard Worker }
391*d57664e9SAndroid Build Coastguard Worker
putLong(uint32_t row,uint32_t column,int64_t value)392*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
393*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
394*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
395*d57664e9SAndroid Build Coastguard Worker }
396*d57664e9SAndroid Build Coastguard Worker
397*d57664e9SAndroid Build Coastguard Worker FieldSlot* fieldSlot = getFieldSlot(row, column);
398*d57664e9SAndroid Build Coastguard Worker if (!fieldSlot) {
399*d57664e9SAndroid Build Coastguard Worker return BAD_VALUE;
400*d57664e9SAndroid Build Coastguard Worker }
401*d57664e9SAndroid Build Coastguard Worker
402*d57664e9SAndroid Build Coastguard Worker fieldSlot->type = FIELD_TYPE_INTEGER;
403*d57664e9SAndroid Build Coastguard Worker fieldSlot->data.l = value;
404*d57664e9SAndroid Build Coastguard Worker return OK;
405*d57664e9SAndroid Build Coastguard Worker }
406*d57664e9SAndroid Build Coastguard Worker
putDouble(uint32_t row,uint32_t column,double value)407*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
408*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
409*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
410*d57664e9SAndroid Build Coastguard Worker }
411*d57664e9SAndroid Build Coastguard Worker
412*d57664e9SAndroid Build Coastguard Worker FieldSlot* fieldSlot = getFieldSlot(row, column);
413*d57664e9SAndroid Build Coastguard Worker if (!fieldSlot) {
414*d57664e9SAndroid Build Coastguard Worker return BAD_VALUE;
415*d57664e9SAndroid Build Coastguard Worker }
416*d57664e9SAndroid Build Coastguard Worker
417*d57664e9SAndroid Build Coastguard Worker fieldSlot->type = FIELD_TYPE_FLOAT;
418*d57664e9SAndroid Build Coastguard Worker fieldSlot->data.d = value;
419*d57664e9SAndroid Build Coastguard Worker return OK;
420*d57664e9SAndroid Build Coastguard Worker }
421*d57664e9SAndroid Build Coastguard Worker
putNull(uint32_t row,uint32_t column)422*d57664e9SAndroid Build Coastguard Worker status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
423*d57664e9SAndroid Build Coastguard Worker if (mReadOnly) {
424*d57664e9SAndroid Build Coastguard Worker return INVALID_OPERATION;
425*d57664e9SAndroid Build Coastguard Worker }
426*d57664e9SAndroid Build Coastguard Worker
427*d57664e9SAndroid Build Coastguard Worker FieldSlot* fieldSlot = getFieldSlot(row, column);
428*d57664e9SAndroid Build Coastguard Worker if (!fieldSlot) {
429*d57664e9SAndroid Build Coastguard Worker return BAD_VALUE;
430*d57664e9SAndroid Build Coastguard Worker }
431*d57664e9SAndroid Build Coastguard Worker
432*d57664e9SAndroid Build Coastguard Worker fieldSlot->type = FIELD_TYPE_NULL;
433*d57664e9SAndroid Build Coastguard Worker fieldSlot->data.buffer.offset = 0;
434*d57664e9SAndroid Build Coastguard Worker fieldSlot->data.buffer.size = 0;
435*d57664e9SAndroid Build Coastguard Worker return OK;
436*d57664e9SAndroid Build Coastguard Worker }
437*d57664e9SAndroid Build Coastguard Worker
438*d57664e9SAndroid Build Coastguard Worker }; // namespace android
439