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