xref: /aosp_15_r20/frameworks/base/libs/androidfw/CursorWindow.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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