xref: /aosp_15_r20/frameworks/native/libs/binder/BufferedTextOutput.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright (C) 2006 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker #include "BufferedTextOutput.h"
18*38e8c45fSAndroid Build Coastguard Worker 
19*38e8c45fSAndroid Build Coastguard Worker #include <cutils/atomic.h>
20*38e8c45fSAndroid Build Coastguard Worker #include <utils/Log.h>
21*38e8c45fSAndroid Build Coastguard Worker #include <utils/RefBase.h>
22*38e8c45fSAndroid Build Coastguard Worker #include <utils/Vector.h>
23*38e8c45fSAndroid Build Coastguard Worker 
24*38e8c45fSAndroid Build Coastguard Worker #include <pthread.h>
25*38e8c45fSAndroid Build Coastguard Worker #include <stdio.h>
26*38e8c45fSAndroid Build Coastguard Worker #include <stdlib.h>
27*38e8c45fSAndroid Build Coastguard Worker 
28*38e8c45fSAndroid Build Coastguard Worker #include "Debug.h"
29*38e8c45fSAndroid Build Coastguard Worker #include "Static.h"
30*38e8c45fSAndroid Build Coastguard Worker 
31*38e8c45fSAndroid Build Coastguard Worker // ---------------------------------------------------------------------------
32*38e8c45fSAndroid Build Coastguard Worker 
33*38e8c45fSAndroid Build Coastguard Worker namespace android {
34*38e8c45fSAndroid Build Coastguard Worker 
35*38e8c45fSAndroid Build Coastguard Worker struct BufferedTextOutput::BufferState : public RefBase
36*38e8c45fSAndroid Build Coastguard Worker {
BufferStateandroid::BufferedTextOutput::BufferState37*38e8c45fSAndroid Build Coastguard Worker     explicit BufferState(int32_t _seq)
38*38e8c45fSAndroid Build Coastguard Worker         : seq(_seq)
39*38e8c45fSAndroid Build Coastguard Worker         , buffer(nullptr)
40*38e8c45fSAndroid Build Coastguard Worker         , bufferPos(0)
41*38e8c45fSAndroid Build Coastguard Worker         , bufferSize(0)
42*38e8c45fSAndroid Build Coastguard Worker         , atFront(true)
43*38e8c45fSAndroid Build Coastguard Worker         , indent(0)
44*38e8c45fSAndroid Build Coastguard Worker         , bundle(0) {
45*38e8c45fSAndroid Build Coastguard Worker     }
~BufferStateandroid::BufferedTextOutput::BufferState46*38e8c45fSAndroid Build Coastguard Worker     ~BufferState() {
47*38e8c45fSAndroid Build Coastguard Worker         free(buffer);
48*38e8c45fSAndroid Build Coastguard Worker     }
49*38e8c45fSAndroid Build Coastguard Worker 
appendandroid::BufferedTextOutput::BufferState50*38e8c45fSAndroid Build Coastguard Worker     status_t append(const char* txt, size_t len) {
51*38e8c45fSAndroid Build Coastguard Worker         if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow
52*38e8c45fSAndroid Build Coastguard Worker         if ((len+bufferPos) > bufferSize) {
53*38e8c45fSAndroid Build Coastguard Worker             if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow
54*38e8c45fSAndroid Build Coastguard Worker             size_t newSize = ((len+bufferPos)*3)/2;
55*38e8c45fSAndroid Build Coastguard Worker             void* b = realloc(buffer, newSize);
56*38e8c45fSAndroid Build Coastguard Worker             if (!b) return NO_MEMORY;
57*38e8c45fSAndroid Build Coastguard Worker             buffer = (char*)b;
58*38e8c45fSAndroid Build Coastguard Worker             bufferSize = newSize;
59*38e8c45fSAndroid Build Coastguard Worker         }
60*38e8c45fSAndroid Build Coastguard Worker         memcpy(buffer+bufferPos, txt, len);
61*38e8c45fSAndroid Build Coastguard Worker         bufferPos += len;
62*38e8c45fSAndroid Build Coastguard Worker         return NO_ERROR;
63*38e8c45fSAndroid Build Coastguard Worker     }
64*38e8c45fSAndroid Build Coastguard Worker 
restartandroid::BufferedTextOutput::BufferState65*38e8c45fSAndroid Build Coastguard Worker     void restart() {
66*38e8c45fSAndroid Build Coastguard Worker         bufferPos = 0;
67*38e8c45fSAndroid Build Coastguard Worker         atFront = true;
68*38e8c45fSAndroid Build Coastguard Worker         if (bufferSize > 256) {
69*38e8c45fSAndroid Build Coastguard Worker             void* b = realloc(buffer, 256);
70*38e8c45fSAndroid Build Coastguard Worker             if (b) {
71*38e8c45fSAndroid Build Coastguard Worker                 buffer = (char*)b;
72*38e8c45fSAndroid Build Coastguard Worker                 bufferSize = 256;
73*38e8c45fSAndroid Build Coastguard Worker             }
74*38e8c45fSAndroid Build Coastguard Worker         }
75*38e8c45fSAndroid Build Coastguard Worker     }
76*38e8c45fSAndroid Build Coastguard Worker 
77*38e8c45fSAndroid Build Coastguard Worker     const int32_t seq;
78*38e8c45fSAndroid Build Coastguard Worker     char* buffer;
79*38e8c45fSAndroid Build Coastguard Worker     size_t bufferPos;
80*38e8c45fSAndroid Build Coastguard Worker     size_t bufferSize;
81*38e8c45fSAndroid Build Coastguard Worker     bool atFront;
82*38e8c45fSAndroid Build Coastguard Worker     int32_t indent;
83*38e8c45fSAndroid Build Coastguard Worker     int32_t bundle;
84*38e8c45fSAndroid Build Coastguard Worker };
85*38e8c45fSAndroid Build Coastguard Worker 
86*38e8c45fSAndroid Build Coastguard Worker struct BufferedTextOutput::ThreadState
87*38e8c45fSAndroid Build Coastguard Worker {
88*38e8c45fSAndroid Build Coastguard Worker     Vector<sp<BufferedTextOutput::BufferState> > states;
89*38e8c45fSAndroid Build Coastguard Worker };
90*38e8c45fSAndroid Build Coastguard Worker 
91*38e8c45fSAndroid Build Coastguard Worker static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
92*38e8c45fSAndroid Build Coastguard Worker 
93*38e8c45fSAndroid Build Coastguard Worker static volatile int32_t gSequence = 0;
94*38e8c45fSAndroid Build Coastguard Worker 
95*38e8c45fSAndroid Build Coastguard Worker static volatile int32_t gFreeBufferIndex = -1;
96*38e8c45fSAndroid Build Coastguard Worker 
allocBufferIndex()97*38e8c45fSAndroid Build Coastguard Worker static int32_t allocBufferIndex()
98*38e8c45fSAndroid Build Coastguard Worker {
99*38e8c45fSAndroid Build Coastguard Worker     int32_t res = -1;
100*38e8c45fSAndroid Build Coastguard Worker 
101*38e8c45fSAndroid Build Coastguard Worker     pthread_mutex_lock(&gMutex);
102*38e8c45fSAndroid Build Coastguard Worker 
103*38e8c45fSAndroid Build Coastguard Worker     if (gFreeBufferIndex >= 0) {
104*38e8c45fSAndroid Build Coastguard Worker         res = gFreeBufferIndex;
105*38e8c45fSAndroid Build Coastguard Worker         gFreeBufferIndex = gTextBuffers[res];
106*38e8c45fSAndroid Build Coastguard Worker         gTextBuffers.editItemAt(res) = -1;
107*38e8c45fSAndroid Build Coastguard Worker 
108*38e8c45fSAndroid Build Coastguard Worker     } else {
109*38e8c45fSAndroid Build Coastguard Worker         res = gTextBuffers.size();
110*38e8c45fSAndroid Build Coastguard Worker         gTextBuffers.add(-1);
111*38e8c45fSAndroid Build Coastguard Worker     }
112*38e8c45fSAndroid Build Coastguard Worker 
113*38e8c45fSAndroid Build Coastguard Worker     pthread_mutex_unlock(&gMutex);
114*38e8c45fSAndroid Build Coastguard Worker 
115*38e8c45fSAndroid Build Coastguard Worker     return res;
116*38e8c45fSAndroid Build Coastguard Worker }
117*38e8c45fSAndroid Build Coastguard Worker 
freeBufferIndex(int32_t idx)118*38e8c45fSAndroid Build Coastguard Worker static void freeBufferIndex(int32_t idx)
119*38e8c45fSAndroid Build Coastguard Worker {
120*38e8c45fSAndroid Build Coastguard Worker     pthread_mutex_lock(&gMutex);
121*38e8c45fSAndroid Build Coastguard Worker     gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
122*38e8c45fSAndroid Build Coastguard Worker     gFreeBufferIndex = idx;
123*38e8c45fSAndroid Build Coastguard Worker     pthread_mutex_unlock(&gMutex);
124*38e8c45fSAndroid Build Coastguard Worker }
125*38e8c45fSAndroid Build Coastguard Worker 
126*38e8c45fSAndroid Build Coastguard Worker // ---------------------------------------------------------------------------
127*38e8c45fSAndroid Build Coastguard Worker 
BufferedTextOutput(uint32_t flags)128*38e8c45fSAndroid Build Coastguard Worker BufferedTextOutput::BufferedTextOutput(uint32_t flags)
129*38e8c45fSAndroid Build Coastguard Worker     : mFlags(flags)
130*38e8c45fSAndroid Build Coastguard Worker     , mSeq(android_atomic_inc(&gSequence))
131*38e8c45fSAndroid Build Coastguard Worker     , mIndex(allocBufferIndex())
132*38e8c45fSAndroid Build Coastguard Worker {
133*38e8c45fSAndroid Build Coastguard Worker     mGlobalState = new BufferState(mSeq);
134*38e8c45fSAndroid Build Coastguard Worker     if (mGlobalState) mGlobalState->incStrong(this);
135*38e8c45fSAndroid Build Coastguard Worker }
136*38e8c45fSAndroid Build Coastguard Worker 
~BufferedTextOutput()137*38e8c45fSAndroid Build Coastguard Worker BufferedTextOutput::~BufferedTextOutput()
138*38e8c45fSAndroid Build Coastguard Worker {
139*38e8c45fSAndroid Build Coastguard Worker     if (mGlobalState) mGlobalState->decStrong(this);
140*38e8c45fSAndroid Build Coastguard Worker     freeBufferIndex(mIndex);
141*38e8c45fSAndroid Build Coastguard Worker }
142*38e8c45fSAndroid Build Coastguard Worker 
print(const char * txt,size_t len)143*38e8c45fSAndroid Build Coastguard Worker status_t BufferedTextOutput::print(const char* txt, size_t len)
144*38e8c45fSAndroid Build Coastguard Worker {
145*38e8c45fSAndroid Build Coastguard Worker     //printf("BufferedTextOutput: printing %d\n", len);
146*38e8c45fSAndroid Build Coastguard Worker 
147*38e8c45fSAndroid Build Coastguard Worker     AutoMutex _l(mLock);
148*38e8c45fSAndroid Build Coastguard Worker     BufferState* b = getBuffer();
149*38e8c45fSAndroid Build Coastguard Worker 
150*38e8c45fSAndroid Build Coastguard Worker     const char* const end = txt+len;
151*38e8c45fSAndroid Build Coastguard Worker 
152*38e8c45fSAndroid Build Coastguard Worker     status_t err;
153*38e8c45fSAndroid Build Coastguard Worker 
154*38e8c45fSAndroid Build Coastguard Worker     while (txt < end) {
155*38e8c45fSAndroid Build Coastguard Worker         // Find the next line.
156*38e8c45fSAndroid Build Coastguard Worker         const char* first = txt;
157*38e8c45fSAndroid Build Coastguard Worker         while (txt < end && *txt != '\n') txt++;
158*38e8c45fSAndroid Build Coastguard Worker 
159*38e8c45fSAndroid Build Coastguard Worker         // Include this and all following empty lines.
160*38e8c45fSAndroid Build Coastguard Worker         while (txt < end && *txt == '\n') txt++;
161*38e8c45fSAndroid Build Coastguard Worker 
162*38e8c45fSAndroid Build Coastguard Worker         // Special cases for first data on a line.
163*38e8c45fSAndroid Build Coastguard Worker         if (b->atFront) {
164*38e8c45fSAndroid Build Coastguard Worker             if (b->indent > 0) {
165*38e8c45fSAndroid Build Coastguard Worker                 // If this is the start of a line, add the indent.
166*38e8c45fSAndroid Build Coastguard Worker                 const char* prefix = stringForIndent(b->indent);
167*38e8c45fSAndroid Build Coastguard Worker                 err = b->append(prefix, strlen(prefix));
168*38e8c45fSAndroid Build Coastguard Worker                 if (err != NO_ERROR) return err;
169*38e8c45fSAndroid Build Coastguard Worker 
170*38e8c45fSAndroid Build Coastguard Worker             } else if (*(txt-1) == '\n' && !b->bundle) {
171*38e8c45fSAndroid Build Coastguard Worker                 // Fast path: if we are not indenting or bundling, and
172*38e8c45fSAndroid Build Coastguard Worker                 // have been given one or more complete lines, just write
173*38e8c45fSAndroid Build Coastguard Worker                 // them out without going through the buffer.
174*38e8c45fSAndroid Build Coastguard Worker 
175*38e8c45fSAndroid Build Coastguard Worker                 // Slurp up all of the lines.
176*38e8c45fSAndroid Build Coastguard Worker                 const char* lastLine = txt;
177*38e8c45fSAndroid Build Coastguard Worker                 while (txt < end) {
178*38e8c45fSAndroid Build Coastguard Worker                     if (*txt++ == '\n') lastLine = txt;
179*38e8c45fSAndroid Build Coastguard Worker                 }
180*38e8c45fSAndroid Build Coastguard Worker                 struct iovec vec;
181*38e8c45fSAndroid Build Coastguard Worker                 vec.iov_base = (void*)first;
182*38e8c45fSAndroid Build Coastguard Worker                 vec.iov_len = lastLine-first;
183*38e8c45fSAndroid Build Coastguard Worker                 //printf("Writing %d bytes of data!\n", vec.iov_len);
184*38e8c45fSAndroid Build Coastguard Worker                 writeLines(vec, 1);
185*38e8c45fSAndroid Build Coastguard Worker                 txt = lastLine;
186*38e8c45fSAndroid Build Coastguard Worker                 continue;
187*38e8c45fSAndroid Build Coastguard Worker             }
188*38e8c45fSAndroid Build Coastguard Worker         }
189*38e8c45fSAndroid Build Coastguard Worker 
190*38e8c45fSAndroid Build Coastguard Worker         // Append the new text to the buffer.
191*38e8c45fSAndroid Build Coastguard Worker         err = b->append(first, txt-first);
192*38e8c45fSAndroid Build Coastguard Worker         if (err != NO_ERROR) return err;
193*38e8c45fSAndroid Build Coastguard Worker         b->atFront = *(txt-1) == '\n';
194*38e8c45fSAndroid Build Coastguard Worker 
195*38e8c45fSAndroid Build Coastguard Worker         // If we have finished a line and are not bundling, write
196*38e8c45fSAndroid Build Coastguard Worker         // it out.
197*38e8c45fSAndroid Build Coastguard Worker         //printf("Buffer is now %d bytes\n", b->bufferPos);
198*38e8c45fSAndroid Build Coastguard Worker         if (b->atFront && !b->bundle) {
199*38e8c45fSAndroid Build Coastguard Worker             struct iovec vec;
200*38e8c45fSAndroid Build Coastguard Worker             vec.iov_base = b->buffer;
201*38e8c45fSAndroid Build Coastguard Worker             vec.iov_len = b->bufferPos;
202*38e8c45fSAndroid Build Coastguard Worker             //printf("Writing %d bytes of data!\n", vec.iov_len);
203*38e8c45fSAndroid Build Coastguard Worker             writeLines(vec, 1);
204*38e8c45fSAndroid Build Coastguard Worker             b->restart();
205*38e8c45fSAndroid Build Coastguard Worker         }
206*38e8c45fSAndroid Build Coastguard Worker     }
207*38e8c45fSAndroid Build Coastguard Worker 
208*38e8c45fSAndroid Build Coastguard Worker     return NO_ERROR;
209*38e8c45fSAndroid Build Coastguard Worker }
210*38e8c45fSAndroid Build Coastguard Worker 
moveIndent(int delta)211*38e8c45fSAndroid Build Coastguard Worker void BufferedTextOutput::moveIndent(int delta)
212*38e8c45fSAndroid Build Coastguard Worker {
213*38e8c45fSAndroid Build Coastguard Worker     AutoMutex _l(mLock);
214*38e8c45fSAndroid Build Coastguard Worker     BufferState* b = getBuffer();
215*38e8c45fSAndroid Build Coastguard Worker     b->indent += delta;
216*38e8c45fSAndroid Build Coastguard Worker     if (b->indent < 0) b->indent = 0;
217*38e8c45fSAndroid Build Coastguard Worker }
218*38e8c45fSAndroid Build Coastguard Worker 
pushBundle()219*38e8c45fSAndroid Build Coastguard Worker void BufferedTextOutput::pushBundle()
220*38e8c45fSAndroid Build Coastguard Worker {
221*38e8c45fSAndroid Build Coastguard Worker     AutoMutex _l(mLock);
222*38e8c45fSAndroid Build Coastguard Worker     BufferState* b = getBuffer();
223*38e8c45fSAndroid Build Coastguard Worker     b->bundle++;
224*38e8c45fSAndroid Build Coastguard Worker }
225*38e8c45fSAndroid Build Coastguard Worker 
popBundle()226*38e8c45fSAndroid Build Coastguard Worker void BufferedTextOutput::popBundle()
227*38e8c45fSAndroid Build Coastguard Worker {
228*38e8c45fSAndroid Build Coastguard Worker     AutoMutex _l(mLock);
229*38e8c45fSAndroid Build Coastguard Worker     BufferState* b = getBuffer();
230*38e8c45fSAndroid Build Coastguard Worker     b->bundle--;
231*38e8c45fSAndroid Build Coastguard Worker     LOG_FATAL_IF(b->bundle < 0,
232*38e8c45fSAndroid Build Coastguard Worker         "TextOutput::popBundle() called more times than pushBundle()");
233*38e8c45fSAndroid Build Coastguard Worker     if (b->bundle < 0) b->bundle = 0;
234*38e8c45fSAndroid Build Coastguard Worker 
235*38e8c45fSAndroid Build Coastguard Worker     if (b->bundle == 0) {
236*38e8c45fSAndroid Build Coastguard Worker         // Last bundle, write out data if it is complete.  If it is not
237*38e8c45fSAndroid Build Coastguard Worker         // complete, don't write until the last line is done... this may
238*38e8c45fSAndroid Build Coastguard Worker         // or may not be the write thing to do, but it's the easiest.
239*38e8c45fSAndroid Build Coastguard Worker         if (b->bufferPos > 0 && b->atFront) {
240*38e8c45fSAndroid Build Coastguard Worker             struct iovec vec;
241*38e8c45fSAndroid Build Coastguard Worker             vec.iov_base = b->buffer;
242*38e8c45fSAndroid Build Coastguard Worker             vec.iov_len = b->bufferPos;
243*38e8c45fSAndroid Build Coastguard Worker             writeLines(vec, 1);
244*38e8c45fSAndroid Build Coastguard Worker             b->restart();
245*38e8c45fSAndroid Build Coastguard Worker         }
246*38e8c45fSAndroid Build Coastguard Worker     }
247*38e8c45fSAndroid Build Coastguard Worker }
248*38e8c45fSAndroid Build Coastguard Worker 
getBuffer() const249*38e8c45fSAndroid Build Coastguard Worker BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
250*38e8c45fSAndroid Build Coastguard Worker {
251*38e8c45fSAndroid Build Coastguard Worker     if ((mFlags&MULTITHREADED) != 0) {
252*38e8c45fSAndroid Build Coastguard Worker         thread_local ThreadState ts;
253*38e8c45fSAndroid Build Coastguard Worker         while (ts.states.size() <= (size_t)mIndex) ts.states.add(nullptr);
254*38e8c45fSAndroid Build Coastguard Worker         BufferState* bs = ts.states[mIndex].get();
255*38e8c45fSAndroid Build Coastguard Worker         if (bs != nullptr && bs->seq == mSeq) return bs;
256*38e8c45fSAndroid Build Coastguard Worker 
257*38e8c45fSAndroid Build Coastguard Worker         ts.states.editItemAt(mIndex) = sp<BufferState>::make(mIndex);
258*38e8c45fSAndroid Build Coastguard Worker         bs = ts.states[mIndex].get();
259*38e8c45fSAndroid Build Coastguard Worker         if (bs != nullptr) return bs;
260*38e8c45fSAndroid Build Coastguard Worker     }
261*38e8c45fSAndroid Build Coastguard Worker 
262*38e8c45fSAndroid Build Coastguard Worker     return mGlobalState;
263*38e8c45fSAndroid Build Coastguard Worker }
264*38e8c45fSAndroid Build Coastguard Worker 
265*38e8c45fSAndroid Build Coastguard Worker } // namespace android
266